2016-09-20 71 views
1

Struts的版本: 2.5.2Struts2的休息映射器接管非休息JSON結果

Struts的依賴在POM

<dependency> 
    <groupId>org.apache.struts</groupId> 
    <artifactId>struts2-core</artifactId> 
    <version>${org.strutsframework-version}</version> 
</dependency> 


<dependency> 
    <groupId>org.apache.struts</groupId> 
    <artifactId>struts2-convention-plugin</artifactId> 
    <version>${org.strutsframework-version}</version> 
</dependency> 

<dependency> 
    <groupId>org.apache.struts</groupId> 
    <artifactId>struts2-rest-plugin</artifactId> 
    <version>${org.strutsframework-version}</version> 
</dependency> 

<dependency> 
    <groupId>org.apache.struts</groupId> 
    <artifactId>struts2-spring-plugin</artifactId> 
    <version>${org.strutsframework-version}</version> 
</dependency> 

Struts的XML

<?xml version="1.0" encoding="UTF-8"?> 

<!DOCTYPE struts PUBLIC 
     "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" 
     "http://struts.apache.org/dtds/struts-2.3.dtd"> 

<struts> 
    <!-- Tell jinjava where the templates are --> 
    <constant name="struts.jinjava.basepath" value="WEB-INF/jinjava" /> 
    <!-- custom jinjava tags specific to iws --> 
    <constant name="struts.jinjava.scan.tagPackage" value="com.hs.iws.jinjava.tag" /> 
    <constant name="struts.jinjava.scan.functionPackage" value="com.hs.iws.jinjava.function" /> 

    <!--Tell struts to use the REST action Mapper--> 
    <!--<constant name="struts.mapper.class" value="rest"/>--> 

    <!-- allow rest and non rest actions to live together --> 
    <constant name="struts.mapper.class" value="org.apache.struts2.dispatcher.mapper.PrefixBasedActionMapper" /> 
    <constant name="struts.mapper.prefixMapping" value=":rest,/grid:struts"/> 
    <constant name="struts.rest.namespace" value="/" /> 

    <constant name="struts.convention.action.suffix" value="Action"/> 
    <constant name="struts.convention.action.mapAllMatches" value="true"/> 
    <constant name="struts.convention.package.locators.basePackage" value="com.hs.iws.actions" /> 

    <!--re-assert the extensions for struts that have been over written by the rest plugin--> 
    <constant name="struts.action.extension" value="xhtml,,json,action"/> 
    <constant name="struts.rest.content.restrictToGET" value="false" /> 

    <!--configure Convention Plugin to find our controllers--> 
    <constant name="struts.convention.default.parent.package" value="iws-default"/> 

    <!-- Spring Configuration --> 
    <!-- <constant name="struts.objectFactory" value="spring" /> --> 
    <constant name="struts.objectFactory.spring.autoWire" value="type" /> 


    <!-- all grid actions should fall under this package --> 
    <package name="iws-grid" namespace="/grid" extends="struts-default,jweb-struts-gson-json,jinjava,datatables"> 
     <interceptors> 

      <interceptor-stack name="iws-datatable-stack"> 
       <interceptor-ref name="exception"/> 
       <interceptor-ref name="alias"/> 
       <interceptor-ref name="servletConfig"/> 
       <interceptor-ref name="i18n"/> 
       <interceptor-ref name="prepare"/> 
       <interceptor-ref name="chain"/> 
       <interceptor-ref name="datetime"/> 
       <interceptor-ref name="staticParams"/> 
       <interceptor-ref name="actionMappingParams"/> 
       <interceptor-ref name="params"/> 
       <interceptor-ref name="gson-json" /> 
       <interceptor-ref name="workflow"> 
        <param name="excludeMethods">input,back,cancel,browse</param> 
       </interceptor-ref> 
       <interceptor-ref name="debugging"/> 
      </interceptor-stack> 

     </interceptors> 

     <default-interceptor-ref name="iws-datatable-stack" /> 
    </package> 


    <package name="iws-default" extends="rest-default, struts-default, jinjava, jweb-struts-gson-json" namespace="/"> 


    </package> 
</struts> 

行動

package com.hs.iws.actions; 

import com.hs.datatables.DataTable10CriteriaQuery; 
import com.hs.datatables.DataTable10Helper; 
import com.hs.iws.model.Users; 
import org.apache.struts2.convention.annotation.*; 

/** 
* Created by Paul on 9/14/2016. 
*/ 
@InterceptorRef(value = "iws-datatable-stack") 
@ParentPackage(value = "iws-grid") 
public class TestGridAction extends DataTable10CriteriaQuery{ 

    @Action(value="/test-grid-json", 
      results={ 
        @Result(name = "success", type = "datatable") 
      } 
    ) 
    public String execute() { 
     return super.execute(); 
    } 

    @Override 
    protected Class<?> getHibernateClass() { 
     return Users.class; 
    } 
} 

我正在使用DataTables JS庫,並試圖爲網格寫入操作。我已經創建了一個API來爲我創建JSON,並且我只需要將它返回。我創建了一個自定義結果來處理這個問題,但在該操作中映射的結果從不運行。無論結果類型如何,只要REST映射器發現應用程序/ json已從客戶端請求,就會嘗試處理它。我在配置中使用了前綴映射,讓所有url的使用/網格繞過其他映射器。它似乎在某些功能上工作,因爲它運行正確的攔截器堆棧並使用@Action註釋信息來映射URL。但是,指定的結果不運行,而是由其他映射器根據我收到的堆棧跟蹤提供。我想完全繞過/格命名空間中任何操作的其餘映射器。我是否在配置中做了一些錯誤,這些配置仍然會導致請求中涉及到的其他操作?

堆棧跟蹤

ERROR RestActionInvocation Exception processing the result. 
net.sf.json.JSONException: java.lang.reflect.InvocationTargetException 
    at net.sf.json.JSONObject._fromBean(JSONObject.java:987) 
    at net.sf.json.JSONObject.fromObject(JSONObject.java:168) 
    at net.sf.json.AbstractJSON._processValue(AbstractJSON.java:265) 
    at net.sf.json.JSONArray._processValue(JSONArray.java:2514) 
    at net.sf.json.JSONArray.processValue(JSONArray.java:2539) 
    at net.sf.json.JSONArray.addValue(JSONArray.java:2526) 
    at net.sf.json.JSONArray._fromCollection(JSONArray.java:1057) 
    at net.sf.json.JSONArray.fromObject(JSONArray.java:123) 
    at net.sf.json.AbstractJSON._processValue(AbstractJSON.java:237) 
    at net.sf.json.JSONObject._processValue(JSONObject.java:2808) 
    at net.sf.json.JSONObject.processValue(JSONObject.java:2874) 
    at net.sf.json.JSONObject.setInternal(JSONObject.java:2889) 
    at net.sf.json.JSONObject.setValue(JSONObject.java:1577) 
    at net.sf.json.JSONObject._fromBean(JSONObject.java:934) 
    at net.sf.json.JSONObject.fromObject(JSONObject.java:168) 
    at net.sf.json.AbstractJSON._processValue(AbstractJSON.java:265) 
    at net.sf.json.JSONObject._processValue(JSONObject.java:2808) 
    at net.sf.json.JSONObject.processValue(JSONObject.java:2874) 
    at net.sf.json.JSONObject.setInternal(JSONObject.java:2889) 
    at net.sf.json.JSONObject.setValue(JSONObject.java:1577) 
    at net.sf.json.JSONObject._fromBean(JSONObject.java:934) 
    at net.sf.json.JSONObject.fromObject(JSONObject.java:168) 
    at net.sf.json.JSONObject.fromObject(JSONObject.java:130) 
    at org.apache.struts2.rest.handler.JsonLibHandler.fromObject(JsonLibHandler.java:72) 
    at org.apache.struts2.rest.DefaultContentTypeHandlerManager.handleResult(DefaultContentTypeHandlerManager.java:181) 
    at org.apache.struts2.rest.RestActionInvocation.executeResult(RestActionInvocation.java:227) 
    at org.apache.struts2.rest.RestActionInvocation.processResult(RestActionInvocation.java:194) 
    at org.apache.struts2.rest.RestActionInvocation.invoke(RestActionInvocation.java:142) 
    at com.opensymphony.xwork2.DefaultActionProxy.execute(DefaultActionProxy.java:154) 
    at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:556) 
    at org.apache.struts2.dispatcher.ExecuteOperations.executeAction(ExecuteOperations.java:81) 
    at org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:113) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) 
    at com.hs.security.SecurityScanner.doFilter(SecurityScanner.java:95) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) 
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) 
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:105) 
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:506) 
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) 
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) 
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:620) 
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) 
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) 
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:1078) 
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) 
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:760) 
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1524) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 
    at java.lang.Thread.run(Thread.java:745) 
Caused by: java.lang.reflect.InvocationTargetException 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:497) 
    at org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:2116) 
    at org.apache.commons.beanutils.PropertyUtilsBean.getSimpleProperty(PropertyUtilsBean.java:1267) 
    at org.apache.commons.beanutils.PropertyUtilsBean.getNestedProperty(PropertyUtilsBean.java:808) 
    at org.apache.commons.beanutils.PropertyUtilsBean.getProperty(PropertyUtilsBean.java:884) 
    at org.apache.commons.beanutils.PropertyUtils.getProperty(PropertyUtils.java:464) 
    at net.sf.json.JSONObject._fromBean(JSONObject.java:918) 
    ... 52 more 
Caused by: java.lang.UnsupportedOperationException: JsonObject 
    at com.google.gson.JsonElement.getAsByte(JsonElement.java:257) 
    ... 62 more 
+0

我發現如果我使用「.action」調用動作,它會調用正確的結果。不知道是否應該要求?也許有人可以更多地瞭解這一點。 –

+0

使用一些調試工具 –

+0

https://struts.apache.org/docs/rest-plugin.html#RESTPlugin-RESTandnon-RESTfulURL%27sTogetherConfiguration。 –

回答

1

我碰到類似的問題。首先,因爲您可能已經知道struts2-rest-plugin可以返回的不僅僅是json,它還可以返回xml和xhtml(通過切換url中的文件擴展名)。令人討厭的原因是你的結果不起作用,strtus2-rest-plugin不使用結果,而是使用ContentTypeHandlers取代任何使用結果的嘗試。

我認爲在你的情況下,.action後綴會破壞rest-plugin,因此它會尋找另一個匹配的動作,可能使用普通的舊約定。

在我自己的代碼中,我創建了一個restful和non-restful軟件包來支持這個問題。我還需要創建自己的內容類型處理程序來替換它們的默認值。可以將自定義結果轉換爲自定義內容類型的處理程序,但除非它是「xml,json或xhtml」類型,否則我認爲創建一個不穩定的程序包並使用它來容納這些操作會更有意義。

如果我今晚有時間,我會提供一個用於創建兩套軟件包的struts.xml的副本。這會很有用,因爲我發現struts2-rest-plugin的配置很脆弱(不太直觀,並且需要的線對我來說並不完全合理,而且猜測比邏輯更多)。

<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE struts PUBLIC 
    "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" 
    "http://struts.apache.org/dtds/struts-2.5.dtd"> 
<struts> 
    <constant name="struts.devMode" value="true" /> 
    <!-- the next two lines are ONLY if you want to override a content handler, and since mine is custom it would be with your own impl, however without pain you can only overrride because I think think the extensions are hard coded... so you can't just add your own, could be wrong --> 
    <bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="flexjson" class="com.kenmcwilliams.s2.result.FlexJsonHandler" /> 
    <constant name="struts.rest.handlerOverride.json" value="flexjson"/> 

    <constant name="struts.action.extension" value="xhtml,,xml,json,action"/> 
    <constant name="struts.mapper.class" value="org.apache.struts2.dispatcher.mapper.PrefixBasedActionMapper" /> 
    <constant name="struts.mapper.prefixMapping" value="/rest:rest,:struts"/> 
    <constant name="struts.convention.action.mapAllMatches" value="true"/> 
    <constant name="struts.convention.default.parent.package" value="my-conventions"/> 
    <constant name="struts.rest.namespace" value="/rest"/> 

    <package name="my-conventions" namespace="/" extends="convention-default" > 
     <result-types> 
      <result-type name="tiles" class="org.apache.struts2.views.tiles.TilesResult"/> 
     </result-types> 
     <!-- Following is required for some reason --> 
     <global-allowed-methods>execute,input,back,cancel,browse,save,delete,list,index,show,create,update,destroy,edit,editNew</global-allowed-methods> 
    </package> 

    <package name="my-rest" namespace="/rest" extends="rest-default"> 
     <result-types> 
      <result-type name="flexjson" class="com.kenmcwilliams.s2.result.FlexJsonResult"/> 
     </result-types> 
    </package> 

    <!-- not needed unless you're planning on using tiles --> 
    <package name="my-tiles" namespace="/tiles" extends="tiles-default" strict-method-invocation="false"> 
     <result-types> 
      <result-type name="tiles" class="org.apache.struts2.views.tiles.TilesResult"/> 
     </result-types> 
    </package> 
</struts> 
+0

'PrefixBasedActionMapper'不應該在任何'ContentTypeHandler'之前處理請求嗎?是的,您確實需要使用'PrefixBasedActionMapper'單獨的包,但不需要創建自定義內容類型處理程序。同意插件配置不是非常直觀,但從文檔複製粘貼應該足以實現您的需要。 –

+0

如果您使用的是struts2-rest-plugin,那麼它將按照其約定映射該操作,而這些約定不允許您使用約定來覆蓋它如何生成內容。覆蓋其行爲的唯一方法是使用自定義內容類型,這是一個巨大的痛苦,很顯然你不想讓休息處理程序解決該行爲。 – Quaternion

+0

我沒有仔細看清楚爲什麼其餘插件仍在處理結果,但如前所述,我將在稍後提供一個工作示例......可以比較兩者。 – Quaternion