Posting JSON to REST API
我正在创建一个RESTAPI,它将接受JSON请求。
我用卷发测试它:
1 | curl -i -POST -H 'Accept: application/json' -d '{"id":1,"pan":11111}' http://localhost:8080/PurchaseAPIServer/api/purchase |
< BR>但出现以下错误:
1 2 3 4 5 6 7 | HTTP/1.1 415 Unsupported Media Type Server: Apache-Coyote/1.1 Content-Type: text/html;charset=utf-8 Content-Length: 1051 Date: Wed, 25 Apr 2012 21:36:14 GMT The server refused this request because the request entity is in a format not supported by the requested resource for the requested method (). |
号
调试时,它甚至不会进入控制器中的创建操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import com.app.model.Purchase; import com.app.service.IPurchaseService; @Controller public class PurchaseController { @Autowired private IPurchaseService purchaseService; @RequestMapping(value ="purchase", method = RequestMethod.GET) @ResponseBody public final List<Purchase> getAll() { return purchaseService.getAll(); } @RequestMapping(value ="purchase", method = RequestMethod.POST) @ResponseStatus( HttpStatus.CREATED ) public void create(@RequestBody final Purchase entity) { purchaseService.addPurchase(entity); } } |
我在appconfig.java中添加了jackson config:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | @Configuration @ComponentScan(basePackages ="com.app") public class AppConfig { @Bean public AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter() { final AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter = new AnnotationMethodHandlerAdapter(); final MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter = new MappingJacksonHttpMessageConverter(); HttpMessageConverter<?>[] httpMessageConverter = { mappingJacksonHttpMessageConverter }; String[] supportedHttpMethods = {"POST","GET","HEAD" }; annotationMethodHandlerAdapter.setMessageConverters(httpMessageConverter); annotationMethodHandlerAdapter.setSupportedMethods(supportedHttpMethods); return annotationMethodHandlerAdapter; } } |
。
我的装备现在工作正常:
1 2 3 4 5 6 7 8 9 | curl -i -H"Content-Type:application/json" -H"Accept:application/json" http://localhost:8080/PurchaseAPIServer/api/purchase HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: application/json Transfer-Encoding: chunked Date: Thu, 26 Apr 2012 21:19:55 GMT [{"id":1,"pan":111}] |
但我在尝试发帖时得到了以下信息:
1 2 3 4 5 6 7 8 9 10 | curl -i -X POST -H"Content-Type:application/json" -H"Accept:application/json" http://localhost:8080/PurchaseAPIServer/api/purchaseMe -d"{"id":2,"pan":122}" HTTP/1.1 400 Bad Request Server: Apache-Coyote/1.1 Content-Type: text/html;charset=utf-8 Content-Length: 971 Date: Thu, 26 Apr 2012 21:29:56 GMT Connection: close The request sent by the client was syntactically incorrect (). |
。
我的模型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | @Entity @XmlRootElement public class Purchase implements Serializable { /** * */ private static final long serialVersionUID = 6603477834338392140L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private Long pan; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getPan() { return pan; } public void setPan(Long pan) { this.pan = pan; } } |
。
知道我哪里出错了吗?
谢谢
正如SDouglass建议的那样,SpringMVC自动检测Jackson并设置一个MappingJacksonHTTPMessageConverter来处理与JSON之间的转换。但我确实需要明确地配置转换器,以使其正常工作,正如他所指出的。
我添加了以下内容,而我的curl get请求正在工作..万岁。
应用配置.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | @Configuration @ComponentScan(basePackages ="com.app") public class AppConfig { @Bean public AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter() { final AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter = new AnnotationMethodHandlerAdapter(); final MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter = new MappingJacksonHttpMessageConverter(); HttpMessageConverter<?>[] httpMessageConverter = { mappingJacksonHttpMessageConverter }; String[] supportedHttpMethods = {"POST","GET","HEAD" }; annotationMethodHandlerAdapter.setMessageConverters(httpMessageConverter); annotationMethodHandlerAdapter.setSupportedMethods(supportedHttpMethods); return annotationMethodHandlerAdapter; } } |
< BR>
1 2 3 4 5 6 7 8 9 | curl -i -H"Content-Type:application/json" -H"Accept:application/json" http://localhost:8080/PurchaseAPIServer/api/purchase HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: application/json Transfer-Encoding: chunked Date: Thu, 26 Apr 2012 21:19:55 GMT [{"id":1,"pan":111}] |
。
但是下面的curl-post仍然不起作用(从不点击控制器操作,也不提供控制台调试信息)。
1 2 3 4 5 6 7 8 9 10 | curl -i -X POST -H"Content-Type:application/json" http://localhost:8080/PurchaseAPIServer/api/purchaseMe -d"{"id":2,"pan":122}" HTTP/1.1 400 Bad Request Server: Apache-Coyote/1.1 Content-Type: text/html;charset=utf-8 Content-Length: 971 Date: Thu, 26 Apr 2012 21:29:56 GMT Connection: close The request sent by the client was syntactically incorrect (). |
所以我添加了logback以开始一些详细的调试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <configuration> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n </pattern> </encoder> </appender> <file>/home/thomas/springApps/purchaseapi.log</file> <encoder> <pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n </pattern> </encoder> </appender> <logger name="org.hibernate" level="DEBUG" /> <logger name="org.springframework" level="TRACE" /> <logger name="org.springframework.transaction" level="INFO" /> <logger name="org.springframework.security" level="INFO" /> <!-- to debug security related issues (DEBUG) --> <logger name="org.springframework.web.servlet.mvc" level="TRACE" /> <!-- some serialization issues are at trace level here: org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod --> <!-- our service --> <logger name="com.app" level="DEBUG" /> <!-- <logger name="com.app" level="INFO" /> --><!-- to follow if setup is being executed --> <root level="INFO"> </root> </configuration> |
。
将跟踪级调试添加到org.springframework.web.servlet.mvc中给出了问题的答案。
1 2 3 4 5 6 7 | 2012-04-28 14:17:44,579 DEBUG [http-bio-8080-exec-3] o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor [AbstractMessageConverterMethodArgumentResolver.java:117] Reading [com.app.model.Purchase] as"application/json" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter@74a14fed] 2012-04-28 14:17:44,604 TRACE [http-bio-8080-exec-3] o.s.w.s.m.m.a.ServletInvocableHandlerMethod [InvocableHandlerMethod.java:159] Error resolving argument [0] [type=com.app.model.Purchase] HandlerMethod details: Controller [com.app.controller.PurchaseController] Method [public void com.app.controller.PurchaseController.create(com.app.model.Purchase)] org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Unexpected character ('p' (code 112)): was expecting double-quote to start field name |
。
我把我的卷发改成了下面的一个,一切都很好:
1 2 3 4 5 | curl -i -X POST -H"Content-Type:application/json" http://localhost:8080/PurchaseAPIServer/api/purchase -d '{"pan":11111}' HTTP/1.1 201 Created Server: Apache-Coyote/1.1 Content-Length: 0 Date: Sat, 28 Apr 2012 13:19:40 GMT |
希望有人觉得这很有用。
如果我没记错的话,Spring文档会说Spring MVC将自动检测类路径上的Jackson,并设置一个mappingJacksonHTTPMessageConverter来处理与JSON之间的转换,但是我认为我遇到过这样的情况,我必须手动/明确地配置该转换器以获取工作中的事情。您可以尝试将此添加到MVC配置XML中:
1 2 3 4 5 6 7 | <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"> </list> </property> </bean> |
号
更新:正是这个加上正确格式化发布的JSON,请参见https://stackoverflow.com/a/10363876/433789
2014年,我想对这个问题进行一些更新,帮助我解决了同样的问题。
更新代码以替换Spring3.2中已弃用的AnnotationMethodHandlerAdapter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | @Configuration public class AppConfig { [cc] @Bean public RequestMappingHandlerAdapter annotationMethodHandlerAdapter() { final RequestMappingHandlerAdapter annotationMethodHandlerAdapter = new RequestMappingHandlerAdapter(); final MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter = new MappingJackson2HttpMessageConverter(); List<HttpMessageConverter<?>> httpMessageConverter = new ArrayList<HttpMessageConverter<?>>(); httpMessageConverter.add(mappingJacksonHttpMessageConverter); String[] supportedHttpMethods = {"POST","GET","HEAD" }; annotationMethodHandlerAdapter.setMessageConverters(httpMessageConverter); annotationMethodHandlerAdapter.setSupportedMethods(supportedHttpMethods); return annotationMethodHandlerAdapter; } } |
[/cc]
HTTP/1.1 415不支持的媒体类型错误
在花了很多小时试图弄明白为什么即使添加了正确的JSON配置,我仍然会得到415个错误之后,我最终意识到问题不在服务器端,而是在客户机端。为了让Spring接受JSON,您必须确保将"content-type:application/json"和"accept:application/json"作为HTTP头的一部分发送。具体来说,它是一个Android应用程序httpurlConnection,我必须将其设置为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public static String doPost(final String urlString,final String requestBodyString) throws IOException { final URL url = new URL(urlString); final HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); try { urlConnection.setReadTimeout(10000 /* milliseconds */); urlConnection.setConnectTimeout(15000 /* milliseconds */); urlConnection.setRequestProperty("Content-Type","application/json"); urlConnection.setRequestProperty("Accept","application/json"); urlConnection.setDoOutput(true); urlConnection.setRequestMethod("POST"); urlConnection.setChunkedStreamingMode(0); urlConnection.connect(); final PrintWriter out = new PrintWriter(urlConnection.getOutputStream()); out.print(requestBodyString); out.close(); final InputStream in = new BufferedInputStream(urlConnection.getInputStream()); final String response = readIt(in); in.close(); //important to close the stream return response; } finally { urlConnection.disconnect(); } } |
号
这里有一个单元测试解决方案,类似于YoramGivon的答案——https://stackoverflow.com/a/22516235/1019307。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | public class JSONFormatTest { MockMvc mockMvc; // The controller used doesn't seem to be important though YMMV @InjectMocks ActivityController controller; @Before public void setup() { MockitoAnnotations.initMocks(this); this.mockMvc = standaloneSetup(controller).setMessageConverters(new MappingJackson2HttpMessageConverter()) .build(); } @Test public void thatSaveNewDataCollectionUsesHttpCreated() throws Exception { String jsonContent = getHereJSON02(); this.mockMvc .perform( post("/data_collections").content(jsonContent).contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isCreated()); } private String getHereJSON01() { return"{"dataCollectionId":0,"name":"Sat_016","type":"httpUploadedFiles"," ... } } |
号
运行单元测试,
在Eclipse中(不确定如何在其他IDE中这样做),单击异常链接,将打开该异常的属性对话框。勾选"已启用"框以打破该异常。
调试单元测试,Eclipse将在异常时中断。检查它会发现问题。在我的例子中,这是因为我的JSON中有两个相同的实体。
我也遇到了同样的问题,通过我的代码中的两个更改解决了这个问题:
我的SpringConfig类中的以下方法,因为我在处理程序拦截器中使用的方法被弃用,并给出了一些问题:
1 2 3 4 5 6 7 8 9 10 11 | public RequestMappingHandlerAdapter RequestMappingHandlerAdapter() { final RequestMappingHandlerAdapter requestMappingHandlerAdapter = new RequestMappingHandlerAdapter(); final MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter = new MappingJacksonHttpMessageConverter(); final String[] supportedHttpMethods = {"POST","GET","HEAD" }; requestMappingHandlerAdapter.getMessageConverters().add(0, mappingJacksonHttpMessageConverter); requestMappingHandlerAdapter.setSupportedMethods(supportedHttpMethods); return requestMappingHandlerAdapter; } |
。
尝试添加post请求中的描述符。也就是说,在EDOCX1[0]中添加标题:
1 | Content-Type: application/json |
如果不添加,不管实际发送什么,
另外,在
我也有同样的问题,我解决了它。
1如该线程中所述,添加mappingjackson2httpmessageconverter(另请参见第4节http://www.baeldung.com/spring-httpmessageconverter-rest)
2使用正确的命令(带转义符号):
1 | curl -i -X POST -H"Content-Type:application/json" -d"{"id":"id1","password":"password1"}" http://localhost:8080/user |
尝试在应用程序配置中添加以下代码
1 2 3 4 5 6 | <mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper" ref="jacksonObjectMapper" /> </bean> </mvc:message-converters> |
号
我经历过一次,最后通过添加jar文件jackson-mapper-asl.jar解决了这个问题。检查是否包含了所有这些依赖项,尽管异常本身并没有告诉您这一点。
而且您确实不需要显式地配置bean,也不需要在@requestmapping语句中放置"consumes"。我用的是弹簧3.1 btw。
contenttype:"application/json"是唯一需要配置的。是的,客户方。