Доступ к RequestBody внутри Spring Portlet Controller с использованием @ResourceMapping и @ModelAttribute

Моя проблема аналогична следующим сообщениям: JSON ajax POST в Spring Portlet Controller @ResourceMapping проблема преобразования и @ResourceMapping, который принимает JSON из запроса Ajax

Я пробовал Tipps там, но безуспешно. У меня есть следующие технологии:

  1. liferay-портал 6.2 CE
  2. пользовательский портлет-плагин для liferay на основе весны 3.0.7
  3. кендо-интерфейс для JSP

На стороне клиента я создаю строковый json-объект с функциональностью kendo-ui для jsp, который отправляется в теле запроса. В настоящее время он содержит только некоторые параметры фильтра (но также может содержать дополнительные параметры для пейджинга на стороне сервера, сортировки, группировки и т. д.).

В инструментах разработчика Firefox тело запроса (полезная нагрузка) выглядит следующим образом:

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

На стороне сервера у меня есть POJO для этой структуры. Я успешно протестировал это в среде Spring Web MVC Servlet. При использовании @RequestBody и Jackson десериализация объекта JSON работает.

Работая в среде liferay-portlet, я не могу использовать @RequestBody и httpServletRequest.

Контроллер выглядит следующим образом:

@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);

}

DataRequestObject не имеет значений. Я вижу все атрибуты, но они пусты. И нет параметра запроса "фильтр" (как и ожидалось)

Вот мой объект DataSourceRequest-Object (аннотация):

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)

Я ищу решение уже несколько недель, но не могу преобразовать JSON-Object (десериализовать?) в DataSourceRequest-Object с помощью контроллера портлета. Я даже понятия не имею, как получить доступ к JSON-String в теле запроса (полезной нагрузке) из портлета-контроллера.

После второго упомянутого сообщения проблема может заключаться в вложенных объектах. Я обратился в поддержку кендо-уи с вопросом, как я могу отправить запрос, чтобы получить формат, описанный в посте. Но они сказали мне, что это невозможно (например, с использованием атрибута parameterMap объекта источника данных), и я должен решить это на стороне сервера.

Первое сообщение описывает решение с помощью @ModelAttribute, но затем я получаю только пустой объект, и когда я пытаюсь получить JSON с помощью @RequestParam, я получаю сообщение об ошибке, что параметра нет в запросе (думаю потому что он в теле)

Я думал о настройке дополнительного RESTFul API на основе сервлета Spring Web MVC — я даже попробовал его, и он работает, но я не уверен, что это действительно имеет смысл, потому что у liferay уже есть RESTFul -АПИ.

Есть ли способ преобразовать объект JSON в объект JAVA внутри контроллера портлета? или нужен ли мне дополнительный API?

Любые советы приветствуются!!


person Thomas Lucas    schedule 04.10.2017    source источник


Ответы (2)


У меня была такая же проблема при сериализации и десериализации Json с помощью Liferay. Решение для меня состояло в том, чтобы отправить json в качестве параметра в форме данных. Таким образом, я смог получить Json следующим методом:

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

Затем используйте API Gson для десериализации:

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

С Гсоном у тебя не будет столько проблем. Вы также можете использовать Gson для сериализации объектов при возврате ваших услуг, это позволит избежать проблем с вложенными объектами, которые Liferay не сериализует должным образом.

Этот код показывает, как отправить Json в качестве тела запроса:

Запрос будет обработан методом «serveResource» в 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);
person Giovani Grifante    schedule 05.10.2017
comment
В моем случае это тоже лайфрей! Ok. Я попробую такой подход. Как вы этого добиваетесь, отправляя объект JSON в виде данных формы. Установка для contentType значения application/x-www-form-urlencoded — это одна часть — что еще необходимо для отправки в виде пары ключ/значение? Можете ли вы опубликовать пример? - person Thomas Lucas; 05.10.2017
comment
это хорошо. Просто отредактировал ответ, чтобы добавить больше информации о коде js. - person Giovani Grifante; 05.10.2017
comment
Решенная проблема приносит 2 новые :-(. Нет, у меня проблемы со специальными символами (особенно &)! Например, одним из атрибутов представленного объекта является URL-адрес вида localhost:8080/image/logo?id=2000**&**t=12345567895. Я закодировал его в Backend, но как могу ли я декодировать в клиенте. У меня также есть атрибуты, содержащие такие символы, как GmbH & Co KG. Как вы решили/решите это. - person Thomas Lucas; 06.10.2017
comment
На самом деле у меня никогда не было этой проблемы. Это может помочь вам stackoverflow.com/questions/25466560/ - person Giovani Grifante; 06.10.2017
comment
Ok! Я просто добавил encodeURIComponent для параметра запроса и использовал URLDecoder на стороне сервера! Я отредактирую свое решение, чтобы отразить это. - person Thomas Lucas; 07.10.2017

Чтобы поделиться своим опытом, вот шаги:

  1. в JS установите для contentType значение application/x-www-form-urlencoded. Это код для кендо-уи (использует jQuery Ajax в фоновом режиме)

    <kendo:dataSource-transport-parameterMap>
        function parameterMap(options,type) { 
            if(type==="read"){
    
                return "osdeFilter=" + encodeURIComponent(JSON.stringify(options));
    
            } else {
    
                return "osdeModels=" + encodeURIComponent(JSON.stringify(options.models));
    
            }
        }
    </kendo:dataSource-transport-parameterMap>
    
  2. Получите параметр и в моем случае используйте Джексона вручную для десериализации строки JSON.

    @ResourceMapping(value = "test") 
    public void searchProviderTest(ResourceRequest request, ResourceResponse response) 
    throws JsonParseException, JsonMappingException, IOException {
    
    String osdeFilter =  URLDecoder.decode(request.getParameter("osdeFilter"),"UTF-8");
    LOGGER.info(">>>>>> JsonOjekt per Parameter übergeben:  " + request.getParameter("osdeFilter"));        
    
    ObjectMapper objectMapper = new ObjectMapper();
    DataSourceRequest dataSourceRequest = objectMapper.readValue(osdeFilter, DataSourceRequest.class);
    LOGGER.info(">>>>>>>> DatasourceRequest: " + dataSourceRequest);
    
    }
    

В отличие от @giovani, мне не нужно отправлять пространство имен портлетов. Для этого необходимо добавить следующую конфигурацию в файл liferay-portlet.xml.

<requires-namespaced-parameters>false</requires-namespaced-parameters>
person Thomas Lucas    schedule 05.10.2017