Spring MVC: Nie deserializuje JSON request body

Pracuję nad projektem Spring MVC i jedno z zadań, które muszę wykonać, wymaga od mnie wysłania ciągu danych JSON przez użytkownika w żądaniu POST. Wiem, że Spring deserializuje JSON używając Jacksona do obiektów, ale jeśli spróbuję czegoś takiego:

@RequestMapping(value = "/test", method = RequestMethod.POST)
public void doSomething(@RequestBody String json) {
    // do something
}

Po prostu otrzymuję z powrotem złe żądanie HTTP 400 ("żądanie wysłane przez Klienta było niepoprawne składniowo.").

Jak mogę uzyskać raw JSON wysłany przez Klienta jako ciąg znaków?

Author: Madbreaks, 2013-05-09

6 answers

Zwykle zobaczysz ten typ błędu, gdy Spring MVC znajdzie mapowanie żądania, które pasuje do ścieżki URL, ale parametry (lub nagłówki lub coś takiego) nie pasują do oczekiwanej metody obsługi.

Jeśli używasz adnotacji @ RequestBody, to wierzę, że Spring MVC spodziewa się zmapować całe ciało żądania POST do obiektu. Zgaduję, że twoje ciało nie jest zwykłym ciągiem, ale jakimś pełnym obiektem JSON.

Jeśli masz model Javy obiektu JSON jesteś spodziewając się wtedy możesz zastąpić parametr String tym w deklaracji doSomething, np.

public void doSomething(@RequestBody MyObject myobj) {

Jeśli nie masz obiektu Java, który pasuje do JSON, możesz spróbować go uruchomić, zastępując typ String na Map<String, Object> i zobaczyć, czy to zbliży cię do działającego rozwiązania.

Możesz również włączyć logowanie debugowania w Spring MVC, aby uzyskać więcej informacji o tym, dlaczego było to złe żądanie.

Edit: Biorąc pod uwagę twoje wymagania w komentarze, możesz po prostu wstrzyknąć HttpServletRequest do swojej metody i przeczytać ciało samodzielnie.

public void doSomething(HttpServletRequest request) {
  String jsonBody = IOUtils.toString( request.getInputStream());
  // do stuff
}
 51
Author: digitaljoel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-09 19:34:08

Mieliśmy sytuację, w której chcieliśmy, aby niektóre metody kontrolera mapowały ciałoPOST dobeans , i inne metody, w których chcieliśmy tylko surowyString . Aby to osiągnąć za pomocą @RequestBody adnotacja , musisz skonfigurować wiele konwerterów wiadomości, Coś w tym stylu...

<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>

Następnie, żądania różnych metod muszą określać nagłówek" content-type " z odpowiednią wartością. Dla tych metod, w których ciało żądania jest mapowane do JAXB bean , określ "application/xml". A dla tych, gdzie ciało żądania jest String , Użyj "text/plain".

 6
Author: guest,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-28 05:27:58

Możesz spróbować całkowicie uniknąć @RequestBody i zamiast tego chwycić ciało żądania bezpośrednio przez InputStream/Reader lub WebRequest/HttpServletRequest.

 2
Author: pimlottc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-06 01:33:34

W moim przypadku jest to spowodowane tym, że json nie zacytował nazw pól. Przykład, nie jest to akceptowane:

{ entity: "OneEntity"} 

Ale ten TAK:

{ "entity": "OneEntity"}

Nie znalazłem jeszcze Jak skonfigurować mapowanie obiektów w kontekście spring. Wiem, że jest JsonParser.Cecha.ALLOW_UNQUOTED_FIELD_NAMES ale nie wiem jak ustawić to dla object mapper.

 1
Author: Toni Gamez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-26 12:27:51

Jeśli Twój Content-type to "application / json", a twój pierwszy messageConvertor to nie org.springframework.http.konwerter.StringHttpMessageConverter, Spring nie może działać poprawnie. W moim przypadku zrobiłem to:

<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>
 0
Author: wfh6732,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-12 10:47:10

Dla mnie z aktualizacją wersji wiosennej było to po prostu " konieczne teraz. "XXX" zamiast XXX i wszystko działa dobrze, jak to masz. Content-Type application / json

 0
Author: user5101998,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-06 16:13:08