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

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, отправленный клиентом в виде строки?

4b9b3361

Ответ 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