Search code examples
javajsonreststruts2struts2-rest-plugin

REST API plugin - use body instead of query string for parameters


I'm using this as a reference to create a REST only configuration on Struts2:

https://cwiki.apache.org/confluence/display/WW/REST+Plugin

I have one model, Receipt with a few test fields: title, body.

Currently to create a receipt, I send a request in this way:

POST /receipt/?body=new_body&title=new_title

and it creates me a receipt with the new body and title passed in.

This doesn't work:

POST /receipt/
{
  "body": "new_body",
  "title": "new title"
}

Here's some code:

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>

    <bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="jackson" class="org.apache.struts2.rest.handler.JacksonLibHandler"/>
    <constant name="struts.rest.handlerOverride.json" value="jackson"/>

    <constant name="struts.enable.DynamicMethodInvocation" value="false"/>
    <constant name="struts.devMode" value="true"/>
    <constant name="struts.rest.content.restrictToGET" value="false"/>
    <constant name="struts.rest.defaultExtension" value="json"/>
    <constant name="struts.rest.handlerOverride.EXTENSION" value="json"/>
    <constant name="struts.i18n.encoding" value="UTF-8"/>

    <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="/receipt:rest,:struts"/>

    <constant name="struts.convention.action.suffix" value="Controller"/>
    <constant name="struts.convention.action.mapAllMatches" value="true"/>
    <constant name="struts.convention.default.parent.package" value="receipto"/>
    <constant name="struts.convention.package.locators" value="controllers,actions"/>
</struts>

ReceiptController.java:

public class ReceiptController implements ModelDriven<Object> {

    private ReceiptManager receiptManager = new ReceiptManager();
    private String id;
    private Receipt model = new Receipt();
    private Collection list;

    public Object getModel()
    {
        return (list==null ? model : list);
    }

    public HttpHeaders create()
    {
        receiptManager.save(model);
        return new DefaultHttpHeaders("create");
    }


    public HttpHeaders show()
    {
        model = receiptManager.find(id);
        return new DefaultHttpHeaders("show");
    }

    public HttpHeaders update()
    {
        receiptManager.save(model);
        return new DefaultHttpHeaders("update");
    }

    public HttpHeaders destroy()
    {
        model = receiptManager.destroy(id);
        return new DefaultHttpHeaders("destroy");
    }

    public HttpHeaders index()
    {
        list = receiptManager.list();
        return new DefaultHttpHeaders("index").disableCaching();
    }

    public String getId()
    {
        return id;
    }

    public void setId(String id)
    {
        this.id = id;
    }
}

Is it supposed to work as I want it to, or is it just how the plugin works?


Solution

  • I guess that postman is sending JSON in the body of the request and sets the content type application/json. Struts can parse the request if you add json interceptor to the stack.

    <interceptor-stack name="myStack">
        <interceptor-ref name="json"/>
        <interceptor-ref name="myInterceptor"/>
        <interceptor-ref name="defaultStack"/>
    </interceptor-stack>
    

    The description for "json" interceptor in the JSON Plugin:

    If the interceptor is used, the action will be populated from the JSON content in the request, these are the rules of the interceptor:

    • The "content-type" must be "application/json"
    • The JSON content must be well formed, see json.org for grammar.
    • Action must have a public "setter" method for fields that must be populated.
    • Supported types for population are: Primitives (int,long...String), Date, List, Map, Primitive Arrays, Other class (more on this later), and Array of Other class.
    • Any object in JSON, that is to be populated inside a list, or a map, will be of type Map (mapping from properties to values), any whole number will be of type Long, any decimal number will be of type Double, and any array of type List.

    Resources: