4

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?

Roman C
  • 49,761
  • 33
  • 66
  • 176
alexg
  • 902
  • 11
  • 37

1 Answers1

4

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:

Roman C
  • 49,761
  • 33
  • 66
  • 176
  • 1
    Excellent, thank you! How do you know all this stuff? I'm just a beginner with struts and java/tomcat development in general. Is there any point of reference you might suggest? any good book describing all this? Thanks – alexg Dec 30 '14 at 00:22
  • 1
    You should read [this](http://stackoverflow.com/a/17898392/573032) answer, I'm sure you'll find a lot of usable information. – Roman C Dec 30 '14 at 09:54
  • apparently the post was deleted. Can you post the information here please? thanks – alexg Jan 05 '15 at 02:12
  • 2
    I've added links to this answer. Enjoy. – Roman C Jan 05 '15 at 09:25
  • If we don't have any other custom interceptors (here `myInterceptor`) to be referenced, adding ` ` would work. – Kavin Raju S Feb 08 '22 at 06:40
  • @KavinRaju It is a placeholder for your interceptor in mind. – Roman C Feb 08 '22 at 09:16
  • @RomanC Can you please look into this issue? I receive this error after adding the `JSON Interceptor`. https://stackoverflow.com/questions/71031450/struts2-rest-api-with-id-is-not-mapped-properly-when-api-request-sent-along-wi – Kavin Raju S Feb 08 '22 at 09:32