2013-12-16 3 views
7

Я пытаюсь построить Spring MVC-контроллер, который получит POSTED-форму с параметром в формате JSON, и Spring автоматически преобразует ее в объект Java.Автоматическое преобразование параметра формы JSON в Spring MVC 4.0

  • тип запроса содержание application/x-www-form-urlencoded
  • Имя параметра, который содержит строку JSON является data.json

Это контроллер:

@Controller 
public class MyController { 
    @RequestMapping(value = "/formHandler", method = RequestMethod.POST) 
    public @ResponseBody String handleSubscription(
     @RequestParam("data.json") MyMessage msg) { 
     logger.debug("id: " + msg.getId()); 
     return "OK"; 
    } 
} 

И это то, что MyMessage объект выглядит так:

public class MyMessage { 
    private String id; 
    // Getter/setter omitted for brevity 
} 

Возможно, не удивительно, отправляя форму с параметром data.json = { "ID": "Hello"} приводит к ошибке HTTP 500 с этим исключением:

org.springframework.beans.ConversionNotSupportedException: 
    Failed to convert value of type 'java.lang.String' to required type 'MyMessage' 
nested exception is java.lang.IllegalStateException: 
    Cannot convert value of type [java.lang.String] to required type [MyMessage]: no matching editors or conversion strategy found 

Если я прочитал MappingJackson2HttpMessageConverter docs правильно, Джексон JSON преобразование инициируется Content-Type application/json, который я, очевидно, не могу использовать, поскольку это форма POST (и я не контролирую часть POSTing).

Возможно ли получить Spring для преобразования строки JSON в экземпляр MyMessage, или я должен просто отказаться от нее, прочитать ее как строку и выполнить преобразование самостоятельно?

+0

й Я не контролирую POSTING part_ Вашего клиент должен следовать спецификациям сервера, а не наоборот. –

+0

Клиент - это сторонний механизм webhook, который я не контролирую. Но ваше заявление вызывает у меня любопытство: есть ли что-то в спецификации HTTP, запрещающей отправку данных JSON с использованием параметра поля формы/сообщения? –

+0

Нет, нет, но с REST это стало обычным иметь его в теле. –

ответ

14

Весна вызывает ваши @RequestMapping методы с отражением. Чтобы разрешить каждый аргумент, он переходит к вызову, он использует реализации HandlerMethodArgumentResolver. Для @RequestParam аннотированных параметров используется RequestParamMethodArgumentResolver. Эта реализация связывает параметр запроса с одним объектом, как правило, String или некоторым типом Number.

Однако ваш прецедент немного редок. Вы редко получаете json в качестве параметра запроса , поэтому я думаю, что вам следует подумать о своем дизайне, но если у вас нет другого выбора, вам необходимо зарегистрировать пользовательский PropertyEditor, который позаботится о преобразовании значения параметра json параметра запроса в ваш собственный тип.

Регистрация простая в аннотированный метод @InitBinder в своем классе @Controller

@InitBinder 
public void initBinder(WebDataBinder dataBinder) { 
    dataBinder.registerCustomEditor(MyMessage.class, new PropertyEditorSupport() { 
     Object value; 
     @Override 
     public Object getValue() { 
      return value; 
     } 

     @Override 
     public void setAsText(String text) throws IllegalArgumentException { 
      value = new Gson().fromJson((String) text, MyMessage.class); 
     } 
    }); 
} 

В данном конкретном случае, нам не нужны все методы интерфейса PropertyEditor, поэтому мы можем использовать PropertyEditorSupport, который является полезным по умолчанию - PropertyEditor. Мы просто реализуем два метода, которым мы заботимся о том, чтобы использовать тот вкус анализатора JSON, который мы хотим. Я использовал Gson, потому что он был доступен.

Когда Spring видит, что у него есть запрошенный параметр запроса, он проверит тип параметра, найдет тип MyMessage и ищите зарегистрированный PropertyEditor для этого типа. Он найдет его, потому что мы его зарегистрировали, и он затем будет использовать его для преобразования значения.

Возможно, вам понадобится реализовать другие методы PropertyEditor в зависимости от того, что вы делаете дальше.

Моя рекомендация - никогда не отправлять JSON в качестве параметра запроса. Задайте свой тип контента запроса application/json и отправьте json в качестве тела запроса. Затем используйте @RequestBody, чтобы разобрать его.

+2

«Моя рекомендация - никогда не отправлять JSON в качестве параметра запроса». Я согласен, но я не контролирую отправляющую сторону, я просто внедряю приемник для стороннего веб-хоккея. –

+0

Это для Mandrill? Я предполагаю, что у них был клиент, который мог получать только параметры формы вместо тела контента. –

+0

@NickSpacek Нет, это механизм для приема вызовов [Unbounce webhook] (http://support.unbounce.com/entries/307685-how-does-the-form-webhook-work). –

4

Вы также можете использовать @RequestPart так:

@RequestMapping(value = "/issues", method = RequestMethod.POST, headers = "Content-Type=multipart/form-data") 
public String uploadIssue(@RequestParam("image") MultipartFile file, @RequestPart("issue") MyMessage issue) 
+0

Это должен быть принятый ответ – dcg

Смежные вопросы