POST request to Struts2 with REST plugin not receiving response

前端 未结 3 1014
终归单人心
终归单人心 2021-01-12 10:22

I have a struts2 application which uses the struts2-rest-plugin v.2.2.3.

Everything is working great when it comes to the routing of the requests to the actions and

相关标签:
3条回答
  • 2021-01-12 10:46

    I encountered the same issue. Have you tried to set in the struts.xml file:

    struts.rest.content.restrictToGET = false
    

    See the last setting on the rest plugin docs

    0 讨论(0)
  • 2021-01-12 11:06

    I actually figured out that it was a line in the rest plugin causing this:

    // don't return any content for PUT, DELETE, and POST where there are no errors
    if (!hasErrors && !"get".equalsIgnoreCase(ServletActionContext.getRequest().getMethod())) {
      target = null;
    }
    

    This is in org.apache.struts2.rest.RestActionInvocation in the selectTarget() method. I find this to be quite annoying as it doesn't really follow the REST architecture, id like the option to be able to return response objects for POST, DELETE and PUT requests in some cases.

    I worked around this by extending RestActionProxyFactory and RestActionInvocation and specifying the use of this in my struts xml like so:

    <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="restOverride" class="uk.co.ratedpeople.tp.rest.RPRestActionProxyFactory" />
    <constant name="struts.actionProxyFactory" value="restOverride" />
    

    This allows me to use the struts plugin throughout while returning object on POST requests.

    RestActionProxyFactory

    public class RPRestActionProxyFactory extends RestActionProxyFactory {
    
        @Override
        public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map extraContext, boolean executeResult, boolean cleanupContext) {
            if (namespace.startsWith(this.namespace)) {
                ActionInvocation inv = new RPRestActionInvocation(extraContext, true);
                container.inject(inv);
                return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
            } else {
                return super.createActionProxy(namespace, actionName, methodName, extraContext, executeResult, cleanupContext);
            }
        }
    
    }
    

    RestActionInvocation

    public class RPRestActionInvocation extends RestActionInvocation {
    
        public RPRestActionInvocation(Map extraContext, boolean pushAction) {
            super(extraContext, pushAction);
        }
    
        @SuppressWarnings("unchecked")
        @Override
        protected void selectTarget() {
    
            // Select target (content to return)
            Throwable e = (Throwable)stack.findValue("exception");
            if (e != null) {
    
                // Exception
                target = e;
                hasErrors = true;
    
            } else if (action instanceof ValidationAware && ((ValidationAware)action).hasErrors()) {
    
                // Error messages
                ValidationAware validationAwareAction = ((ValidationAware)action);
    
                Map errors = new HashMap();
                if (validationAwareAction.getActionErrors().size() > 0) {
                    errors.put("actionErrors", validationAwareAction.getActionErrors());
                }
                if (validationAwareAction.getFieldErrors().size() > 0) {
                    errors.put("fieldErrors", validationAwareAction.getFieldErrors());
                }
                target = errors;
                hasErrors = true;
    
            } else if (action instanceof ModelDriven) {
    
                // Model
                target = ((ModelDriven)action).getModel();
    
            } else {
                target = action;
            }
    
            // don't return any content for PUT, DELETE, and POST where there are no errors
    //      if (!hasErrors && !"get".equalsIgnoreCase(ServletActionContext.getRequest().getMethod())) {
    //          target = null;
    //      }
        }
    
    }
    
    0 讨论(0)
  • 2021-01-12 11:11

    I've used struts actions with mixed result types in the past, returning json, xml, and tiles for instance. I'm not sure if it's the recommended way to do it but it requires some configuration using struts.xml even though conventions are being used. Maybe you've already done this, not sure there isn't enough info provided to tell.

    Struts.xml settings:

    <constant name="struts.convention.default.parent.package" value="restful"/>
    
    <package name="restful"  extends="rest-default, struts-default, json-default">
        <result-types>
            <result-type name="tiles" class="org.apache.struts2.views.tiles.TilesResult" />
            <result-type name="json" class="com.googlecode.jsonplugin.JSONResult"/>
        </result-types>
    
        ....
    </package>
    

    I have setup the extra result types to be used on specific actions later. In the action class you can then setup your result types by action or method.

    Action Class:

    @Results({
        @Result(name = "JsonSuccess", type = "json"),
        @Result(name = "success", type = "tiles", location = "/tickets.tiles")
    })
    
    public class EventController extends RestActionSupport implements ModelDriven<EventBean>{
         ...
    }
    

    Something else to note about json results, I've noticed that when I have a serializable object being returned as a result, if that object contains other complex objects with a getter/setter that returns the embedded object, I will often receive an empty result or no result. I often end up writing json wrapper objects to use for my json results with getters/setters that only return java types (String, int, Boolean, etc) and not the embedded objects. I think that've solved this using delegate getters/setters but I'll have to go back and look at some old code.

    0 讨论(0)
提交回复
热议问题