Spring MVC-почему нельзя использовать @RequestBody и @RequestParam вместе
использование клиента HTTP dev с почтовым запросом и приложением типа контента / x-www-form-urlencoded
1) Только @RequestBody
запрос-localhost: 8080 / SpringMVC / добро пожаловать В 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 / добро пожаловать В 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 / добро пожаловать В 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) выше с измененным положением params
запрос-localhost: 8080 / SpringMVC / добро пожаловать В 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 / добро пожаловать?имя=АБВ В 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";
}
/ / имя - "xyz", а тело - "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' тело пусто
может кто-нибудь объяснить такое поведение?
4 ответов
на @RequestBody
У javadoc государств
Аннотация, указывающая параметр метода, должна быть привязана к телу веб-запроса.
он использует зарегистрированные экземпляры HttpMessageConverter
десериализовать тело запроса в объект аннотированного типа параметра.
и @RequestParam
аннотация, которая указывает, что параметр метода должен быть привязан к веб-запрос параметр.
Spring связывает тело запроса с параметром, аннотированным
@RequestBody
.Spring связывает параметры запроса из тела запроса (параметры в кодировке url) с параметром метода. Весной будет использовать имя параметра, т. е..
name
, чтобы отобразить параметр.параметры разрешаются по порядку. The
@RequestBody
обрабатывается первым. Весна поглотит всеHttpServletRequest
InputStream
. Когда он затем пытается решить@RequestParam
, который по умолчаниюrequired
, в строке запроса нет параметра запроса или того, что осталось от тела запроса, т. е. ничего. Таким образом, он терпит неудачу С 400, потому что запрос не может быть правильно обработан методом обработчика.обработчик
@RequestParam
действует сначала, читая, что он может изHttpServletRequest
InputStream
сопоставить параметр запроса, т. е. вся строка запроса/url-кодированные параметры. Он делает так и получает значениеabc
сопоставляется с параметромname
. Когда обработчик для@RequestBody
выполняется, в теле запроса ничего не осталось, поэтому используемый аргумент-пустая строка.обработчик
@RequestBody
считывает тело и привязывает его к параметру. Обработчик@RequestParam
затем можно получить параметр запроса из строки запроса URL.обработчик
@RequestParam
считывает как из тела, так и из строки запроса URL. Обычно он ставил их вMap
, но так как параметр имеет типString
, Весна сериализуетMap
в качестве значения, разделенные запятыми. Обработчик@RequestBody
тогда, опять же, нечего читать из тела.
Я знаю, что слишком поздно отвечать на этот вопрос, но все же это может помочь кому-то читателям.
Кажется, проблемы с версией. Я запускаю все эти тесты с spring 4.1.4 и обнаружил, что порядок @RequestBody
и @RequestParam
не имеет значения.
- то же, что и ваш результат
- то же, что и ваш результат
- дал
body= "name=abc"
иname = "abc"
- же 3.
-
body ="name=abc"
,name = "xyz,abc"
- то же, что и 5.
вы также можете просто изменить состояние @RequestParam default required на false, чтобы не генерировать код состояния HTTP-ответа 400. Это позволит вам разместить аннотации в любом порядке, который вам нравится.
@RequestParam(required = false)String name
это происходит из-за не очень прямой спецификации сервлета. Если вы работаете с родным HttpServletRequest
реализация вы не можете получить как тело кодирования URL, так и параметры. Весна делает некоторые обходные пути, которые делают его еще более странным и непрозрачным.
в таких случаях 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-запрос сервлета теряет параметры из тела POST после прочтения его один раз