Search code examples
javajsonkendo-uiliferay-6spring-portlet-mvc

Access RequestBody inside Spring Portlet Controller using @ResourceMapping and @ModelAttribute


My problem is similar to the following posts: JSON ajax POST to Spring Portlet Controller @ResourceMapping conversion issue and @ResourceMapping that accepts JSON from Ajax request

I have tried the Tipps there, but without success. I have the following Technologies in place:

  1. liferay-portal 6.2 CE
  2. custom portlet-plugin for liferay based on spring 3.0.7
  3. kendo-ui for jsp

On the client-side I produce a stringified json-Object with the functionality of kendo-ui for jsp, which is submitted in the request body. Currently it contains just some filter-parameters (but it can also contain additional parameters for server-side paging, sorting, grouping,..).

In Firefox developer tools the request-body (payload) looks like following:

{
"filter" : {
    "logic" : "and",
    "filters" : [{
            "field" : "name",
            "value" : ""
        }, {
            "field" : "city",
            "value" : ""
        }, {
            "field" : "zip",
            "value" : ""
        }, {
            "field" : "country",
            "value" : ""
        }
    ]
  }
}

On the server-side I have a POJO for that structure. I tested this in a Spring Web MVC Servlet enviroment with success. Using @RequestBody and Jackson the deserialization of the JSON Object works.

Working in liferay-portlet enviroment I cannot use @RequestBody and httpServletRequest.

The Controller looks like following:

@ResourceMapping(value = "test")
public void searchProviderTest(ResourceRequest request, ResourceResponse response,
        @ModelAttribute("filter") DataSourceRequest dataSourceRequest) {

    LOGGER.info(">>>>>> JsonOjekt per Parameter übergeben:  " + request.getParameter("filter"));
    LOGGER.info(">>>>>>>> DatasourceRequest: " + dataSourceRequest);

}

The DataRequestObject has no values. I see all the attributes, but they are empty. And there is no request parameter "filter" (as exspected)

Here is my DataSourceRequest-Object (abstract):

public class DataSourceRequest {
private int page;
private int pageSize;
private int take;
private int skip;
private List<SortDescriptor> sort;
private List<GroupDescriptor> group;
private List<AggregateDescriptor> aggregate;
private HashMap<String, Object> data;

private FilterDescriptor filter;

public DataSourceRequest() {
    filter = new FilterDescriptor();
    data = new HashMap<String, Object>();
}

...(getters and setters)

public static class FilterDescriptor {
    private String logic;
    private List<FilterDescriptor> filters;
    private String field;
    private Object value;
    private String operator;
    private boolean ignoreCase = true;

    public FilterDescriptor() {
        filters = new ArrayList<FilterDescriptor>();
    }

    ...(getters and setters)

Im am searching for a solution since a few weecks, but I do not get the JSON-Object converted (deserialized?) to the DataSourceRequest-Object using the portlet-controller. I even do not have an idea how to access the JSON-String in the request-body (payload) from the portlet-controller.

Following the second mentioned post, the nested objects might be the problem. I contacted the kendo-ui support with the question, how I can submit the request to get the format described in the post. But they told me, that is not possible (e.g. using parameterMap-attribute of the datasource object)and I have to solve it on the server-side.

The first post describes a solution with @ModelAttribute, but then I get only the empty object-and when I try to get the JSON with @RequestParam I get an error, that the parameter is not in the request (I think because it is in the body)

I was thinking about setting up an additional RESTFul API, based on Spring Web MVC Servlet - I even tried it and it works- but I am not sure if that is really meaningful, because liferay already has a RESTFul -API.

Is there a way to convert the JSON Object to an JAVA Object inside the Portlet Controller ? or Do I need the additional API?

Any tips are welcome!!


Solution

  • I had the same problem while serializing and deserializing Json with Liferay. The solution for me was to send the json as a parameter in a form-data. That way a I was able to retrive the Json with the following method:

    String paramJson = ParamUtil.getString(request, "myJson");
    

    And then make use of Gson api to deserialize:

    new Gson().fromJson(paramJson, MyPOJO.class);
    

    You won't have so many troubles with Gson. You can also use Gson to serialize objects in the return of your services, this will avoid problems with nested objects witch Liferay doesn't serialize properly.

    This code show how to send the Json as request body:

    The request will be processed by method 'serveResource' in a MVCPortlet.

    var portletUrl = Liferay.PortletURL.createResourceURL();
    portletUrl.setPortletId(<portletId>);
    portletUrl.setResourceId('publicar'); // Any identifier
    
    var formData = new FormData();           
    formData.append(<portlet-namespace> + 'myJson', JSON.stringify(object)); 
    var xhr = new XMLHttpRequest();
    xhr.addEventListener('load', callbackSuccess, false);
    xhr.open('POST', urlPortlet);
    xhr.send(formData);