Struts2 Rest Mapper берет на себя результаты без отдыха Json

Версия Struts: 2.5.2

Зависимости 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-адреса использовали /grid для обхода остальных карт. Кажется, он работает в некотором объеме, потому что он запускает правильный стек перехватчиков и использует информацию аннотации @Action для сопоставления URL. Тем не менее, указанный результат НЕ выполняется и вместо этого предоставляется сопоставителем остатка на основе трассировки стека, которую я получаю. Я хотел бы полностью обойти остальные мапперы для любых действий в пространстве имен /grid. Я сделал что-то не так в конфигурации, которая все еще заставляет остальных быть вовлеченными в запрос этих действий?

Трассировки стека

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

1 ответ

Я столкнулся с аналогичной проблемой. Во-первых, как вы, наверное, уже знаете, Struts2-rest-плагин может возвращать больше, чем просто json, он может возвращать и xml, и xhtml (включив расширение файла в url). Раздражающая причина того, что ваш результат не работает, заключается в том, что strtus2-rest-plugin не использует результаты, а использует ContentTypeHandlers, который заменяет любые попытки использовать результаты.

Я думаю, что в вашем случае суффикс.action нарушает плагин rest, и поэтому он ищет другое подходящее действие, возможно, с использованием простых старых соглашений.

В своем собственном коде я создал успокаивающие и не успокоительные пакеты, чтобы обойти эту проблему. Мне также нужно было создать свой собственный обработчик типа контента, чтобы заменить их по умолчанию. Можно превратить пользовательский результат в обработчик пользовательского типа контента, но, если он не относится к типу "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>
Другие вопросы по тегам