Spring MVC: не десериализуйте тело запроса JSON
Я работаю над проектом Spring MVC, и одна из задач, которые мне нужно выполнить, требует, чтобы у меня была строка данных JSON, отправленных пользователем в запросе POST. Я знаю, что Spring будет десериализовать JSON, используя Jackson для объектов, но если я попробую что-то вроде следующего:
@RequestMapping(value = "/test", method = RequestMethod.POST)
public void doSomething(@RequestBody String json) {
// do something
}
Я просто получаю HTTP 400 Bad Request назад ( "Запрос, отправленный клиентом, был синтаксически неправильным." ).
Как я могу получить сырой JSON, отправленный клиентом в виде строки?
Ответы
Ответ 1
Обычно вы видите этот тип ошибки, когда Spring MVC находит сопоставление запросов, которое соответствует пути URL, но параметры (или заголовки или что-то) не соответствуют ожидаемому методу обработчика.
Если вы используете аннотацию @RequestBody, я полагаю, что Spring MVC ожидает сопоставить весь текст запроса POST с объектом. Я предполагаю, что ваше тело - это не просто String, а какой-то полный объект JSON.
Если у вас есть Java-модель объекта JSON, которую вы ожидаете, вы можете заменить параметр String тем, что указано в объявлении doSomething, например
public void doSomething(@RequestBody MyObject myobj) {
Если у вас нет объекта Java, который соответствует JSON, вы можете попытаться заставить его работать, заменив тип String
на Map<String, Object>
и посмотрите, приближает ли это вас к рабочему решению.
Вы также можете включить ведение журнала отладки в Spring MVC, чтобы получить дополнительную информацию о том, почему это был плохой запрос.
Edit:
Учитывая ваши требования в комментариях, вы можете просто ввести HttpServletRequest в свой метод и сами прочитать тело.
public void doSomething(HttpServletRequest request) {
String jsonBody = IOUtils.toString( request.getInputStream());
// do stuff
}
Ответ 2
У нас была ситуация, когда мы хотели, чтобы некоторые методы контроллера отображали тело POST на beans, а также другие методы, где нам просто нужна строка Строка. Чтобы выполнить это, используя аннотацию @RequestBody
, вам нужно настроить несколько преобразователей сообщений, например...
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="useDefaultSuffixPattern" value="false"/>
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonConverter" />
<ref bean="marshallingConverter" />
<ref bean="stringHttpMessageConverter" />
</list>
</property>
</bean>
<bean id="jsonConverter"
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
</bean>
<bean id="marshallingConverter"
class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<constructor-arg ref="jaxb2Marshaller" />
<property name="supportedMediaTypes" value="application/xml"/>
</bean>
<bean id="stringHttpMessageConverter"
class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/plain"/>
</bean>
Затем запросы к различным методам должны указывать заголовок "content-type" с соответствующим значением. Для тех методов, где тело запроса сопоставляется с JAXB bean, укажите "application/xml
". И для тех, где тело запроса является строкой, используйте "text/plain
".
Ответ 3
Вы можете вообще избегать @RequestBody
и вместо этого захватить тело запроса непосредственно через InputStream
/Reader
или WebRequest
/HttpServletRequest
.
Ответ 4
В моем случае это потому, что json не цитировал имена полей.
Например, это не принято:
{ entity: "OneEntity"}
но это да:
{ "entity": "OneEntity"}
Я еще не нашел, как настроить сопоставление объектов в контексте spring.
Я знаю, что есть JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, но я не знаю, как это установить для объекта mapper.
Ответ 5
если ваш Content-type является "application/json", а ваш первый messageConvertor не является org.springframework.http.converter.StringHttpMessageConverter, Spring не может работать правильно. В моем случае я сделал это:
<mvc:annotation-driven>
<mvc:message-converters>
<ref bean="stringHttpMessageConverter" /><!-- 放在前面,对@RequestBody String json 提供支持 -->
<ref bean="mappingJacksonHttpMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 消息转换器 -->
<bean id="stringHttpMessageConverter"
class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<bean class="org.springframework.http.MediaType">
<constructor-arg index="0" value="text" />
<constructor-arg index="1" value="plain" />
<constructor-arg index="2" value="UTF-8" />
</bean>
<bean class="org.springframework.http.MediaType">
<constructor-arg index="0" value="application" />
<constructor-arg index="1" value="json" />
<constructor-arg index="2" value="UTF-8" />
</bean>
</list>
</property>
</bean>
<bean id="mappingJacksonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<bean class="org.springframework.http.MediaType">
<constructor-arg index="0" value="text" />
<constructor-arg index="1" value="plain" />
<constructor-arg index="2" value="UTF-8" />
</bean>
<bean class="org.springframework.http.MediaType">
<constructor-arg index="0" value="application" />
<constructor-arg index="1" value="json" />
<constructor-arg index="2" value="UTF-8" />
</bean>
</list>
</property>
<!-- 设置时间格式, 有了这个就不用在pojo的属性上写了 -->
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss"></constructor-arg>
</bean>
</property>
</bean>
</property>
</bean>
Ответ 6
Для меня с обновлением версии spring это было просто "необходимо сейчас".
"XXX" вместо XXX, и все работает нормально, как у вас есть.
Приложение Content-Type/json