Подтвердить что ты не робот

Spring MVC - Почему бы не использовать @RequestBody и @RequestParam вместе

Использование HTTP-клиента-разработчика с почтовым запросом и приложением Content-Type/x-www-form-urlencoded

1) Только @RequestBody

Запрос - localhost: 8080/SpringMVC/welcome В Body - name = abc

код -

@RequestMapping(method = RequestMethod.POST)
public String printWelcome(@RequestBody String body, Model model) {
    model.addAttribute("message", body);
    return "hello";
}

//Дает тело как 'name = abc', как ожидалось

2) Только @RequestParam

Запрос - localhost: 8080/SpringMVC/welcome В Body - name = abc

код -

@RequestMapping(method = RequestMethod.POST)
public String printWelcome(@RequestParam String name, Model model) {
    model.addAttribute("name", name);
    return "hello";
}

//Дает имя как 'abc', как ожидалось

3) Оба вместе

Запрос - localhost: 8080/SpringMVC/welcome В Body - name = abc

код -

@RequestMapping(method = RequestMethod.POST)
public String printWelcome(@RequestBody String body, @RequestParam String name, Model model) {
    model.addAttribute("name", name);
    model.addAttribute("message", body);
    return "hello";
}

//Код ошибки HTTP 400 - запрос, отправленный клиентом, был синтаксически неправильным.

4) Выше с измененной позицией изменения

Запрос - localhost: 8080/SpringMVC/welcome В Body - name = abc

код -

@RequestMapping(method = RequestMethod.POST)
public String printWelcome(@RequestParam String name, @RequestBody String body, Model model) {
    model.addAttribute("name", name);
    model.addAttribute("message", body);
    return "hello";
}

//Ошибка. Имя - 'abc'. тело пустое

5) Вместе, но получите параметры url типа

Запрос - localhost: 8080/SpringMVC/welcome? name = xyz В Body - name = abc

код -

@RequestMapping(method = RequestMethod.POST)
public String printWelcome(@RequestBody String body, @RequestParam String name, Model model) {
    model.addAttribute("name", name);
    model.addAttribute("message", body);
    return "hello";
}

//name is 'xyz', а body - 'name = abc'

6) То же, что и 5), но с измененной позицией параметров

Код -

@RequestMapping(method = RequestMethod.POST)
public String printWelcome(@RequestParam String name, @RequestBody String body, Model model) {
    model.addAttribute("name", name);
    model.addAttribute("message", body);
    return "hello";
}

//name = 'xyz, abc' body пуст

Может ли кто-нибудь объяснить это поведение?

4b9b3361

Ответ 1

Состояние @RequestBody javadoc

Аннотации, указывающие параметр метода, должны быть привязаны к телу веб-запроса.

Он использует зарегистрированные экземпляры HttpMessageConverter для десериализации тела запроса в объект типа аннотированных параметров.

И @RequestParam

Аннотации, указывающие, что параметр метода должен быть связан с параметр веб-запроса.

  • Spring связывает тело запроса с параметром, аннотированным с помощью @RequestBody.

  • Spring связывает параметры запроса с телом запроса (параметрами, закодированными по URL), с параметром метода. Spring будет использовать имя параметра, т.е. name, чтобы отобразить параметр.

  • Параметры разрешаются по порядку. Сначала обрабатывается @RequestBody. Spring будет потреблять все HttpServletRequest InputStream. Затем, когда он пытается разрешить @RequestParam, который по умолчанию required, в строке запроса отсутствует параметр запроса или то, что осталось от тела запроса, т.е. ничего. Таким образом, он терпит неудачу с 400, потому что запрос не может быть правильно обработан методом обработчика.

  • Обработчик для @RequestParam действует сначала, читая, что может сделать HttpServletRequest InputStream для сопоставления параметра запроса, т.е. все параметры запроса /url -encoded. Он делает это и получает значение abc, отображаемое параметру name. Когда выполняется обработчик для @RequestBody, в теле запроса ничего не осталось, поэтому используемым аргументом является пустая строка.

  • Обработчик для @RequestBody считывает тело и связывает его с параметром. Обработчик для @RequestParam может затем получить параметр запроса из строки запроса URL.

  • Обработчик для @RequestParam считывает как тело, так и строку запроса URL. Обычно они помещают их в Map, но поскольку параметр имеет тип String, Spring будет сериализовать Map как значения, разделенные запятой. Обработчик для @RequestBody, то, опять же, не осталось ничего читать с тела.

Ответ 2

Это происходит из-за не очень прямой спецификации сервлета. Если вы работаете с встроенной реализацией HttpServletRequest, вы не можете получить тело кодировки URL-адреса и параметры. Spring делает некоторые обходные пути, которые делают его еще более странным и непрозрачным.

В таких случаях Spring (версия 3.2.4) повторно отображает тело для вас, используя данные из метода getParameterMap(). Он смешивает параметры GET и POST и нарушает порядок параметров. Класс, который отвечает за хаос, ServletServerHttpRequest. К сожалению, его нельзя заменить, но класс StringHttpMessageConverter может быть.

Чистое решение, к сожалению, не так просто:

  • Замена StringHttpMessageConverter. Копировать/Перезаписать исходный метод настройки класса readInternal().
  • Обтекание HttpServletRequest перезаписи getInputStream(), getReader() и getParameter*() методов.

В методе StringHttpMessageConverter # readInternal следующий код должен использоваться:

    if (inputMessage instanceof ServletServerHttpRequest) {
        ServletServerHttpRequest oo = (ServletServerHttpRequest)inputMessage;
        input = oo.getServletRequest().getInputStream();
    } else {
        input = inputMessage.getBody();
    }

Затем конвертер должен быть зарегистрирован в контексте.

<mvc:annotation-driven>
    <mvc:message-converters register-defaults="true/false">
        <bean class="my-new-converter-class"/>
   </mvc:message-converters>
</mvc:annotation-driven>

Ниже описан второй шаг: Запрос Http Servlet теряет параметры из тела POST после его чтения

Ответ 3

Возможно, слишком поздно ответить на этот вопрос, но я просто ответил, чтобы он помог другим читателям. Это проблемы с версией. Я запускаю все эти тесты с помощью spring 4.1.4 и обнаружил, что порядок @RequestBody и @RequestParam не имеет значения.

  • то же, что и ваш результат
  • то же, что и ваш результат
  • дал body= "name=abc" и name = "abc"
  • То же, что и 3.
  • body ="name=abc", name = "xyz,abc"
  • то же, что и 5.

Ответ 4

Вы также можете просто изменить статус по умолчанию, заданный по умолчанию @RequestParam, на false, чтобы код статуса ответа HTTP не генерировался. Это позволит вам размещать аннотации в любом порядке, который вам нравится.

@RequestParam(required = false)String name