Spring 4.x/3.x (Web MVC) REST API and JSON2 Post requests, how to get it right once for all?
在详细讨论之前,我知道有很多关于stackoverflow的对话和相关问题。所有这些都以不同的方式帮助我,所以我认为我把所有的发现放在一起作为一个单一的有组织的常见问题来总结我的发现。
相关概念当然你知道这些,但我只是写它们作为一个快速回顾。如果我错过了什么,请随意编辑。
HTTP POST请求:当您愿意将对象发送到Web服务或服务器端应用程序时,将使用POST请求。
序列化:是将对象从Web浏览器传递到服务器端应用程序的过程。可以使用jquery-ajax调用或curl-post请求。
序列化协议:现在最流行的是JSON和XML。由于XML标记的性质,序列化XML对象的大小相对较大,因此XML越来越不受欢迎。在这个常见问题解答中,主要的焦点是JSON2序列化。
春天:Spring框架及其强大的注释使得以有效的方式公开Web服务成为可能。春天有很多不同的图书馆。我们关注的是SpringWebMVC。
卷曲与jQuery:这些是您可以用来在客户机端发出POST请求的工具。即使您计划使用jquery-ajax调用,我建议您使用curl进行调试,因为它在发出post请求后为您提供了详细的响应。
@requestbody对@requestparam/@pathvariable对@modelattribute:在有一个不依赖于Java EE模型的Web服务的情况下,必须使用"请求体"。如果您使用的是模型,并且您的JSON对象被添加到模型中,那么您可以通过@modelattribute访问该对象。只有当您的请求是GET请求或GET和POST请求组合时,才需要使用@requestparam/@pathvariable。
@请求主体vs@resposebody:从名称中可以看出,只要在服务器端方法处理请求后向客户机发送响应,就只需要@responsebody。
RequestMappingHandlerAdapter与AnnotationMethodHandlerAdapter之比较:RequestMappingHandlerAdapter是Spring框架的新映射处理程序,它取代了自Spring3.1以来的AnnotationMethodHandlerAdapter。如果您的现有配置仍在AnnotationMethodHandlerAdapter中,您可能会发现此日志很有用。本文中提供的配置将向您介绍如何设置RequestMappingHandlerAdapter。
安装程序您需要设置消息转换器。这就是如何在服务器端将序列化JSON消息体转换为本地Java对象的方法。
基本配置。在基本配置示例中,转换器是marshallinghttpmessageconverter和castormarshaller,我已将它们替换为mappingjackson2httpmessageconverter和mappingjacksonhttpmessageconverter。
在哪里放置配置在设置项目的方式中,我有两个配置文件:
- 应用程序上下文XML:其中一个是应用程序上下文XML文件,您的sessionFactory bean、datasource bean等位于该文件中。
- MVC调度程序servlet xml:这就是您拥有视图解析器bean并导入应用程序上下文XML的地方。
hadleradicator bean必须位于后面的MVC Dispatcher XML文件中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/> <ref bean="jsonConverter"/> </list> </property> <property name="requireSession" value="false"/> </bean> <bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes" value="application/json"/> </bean> |
您可以有多个消息转换器。在这里,我创建了一个普通的JSON和一个JSON2消息转换器。XML文件中的ref和normal bean格式都已被使用(我个人更喜欢将ref标记作为其neater)。
REST API下面是一个示例控制器,它公开了RESTAPI。
控制器这就是HTTP POST请求的RESTAPI公开的地方。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @Component @Controller @RequestMapping("/api/user") public class UserController { @RequestMapping(value ="/add", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public String insertUser(@RequestBody final User user) { System.out.println(user.toString()); userService.insertUser(user); String userAdded ="User-> {" + user.toString() +"} is added"; System.out.println(userAdded); return userAdded; } } |
Java对象
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | @JsonAutoDetect public class User { private int id; private String username; private String name; private String lastName; private String email; public int getId() { return externalId; } public void setId(final int id) { this.id = id; } public String getName() { return name; } public void setName(final String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(final String email) { this.email = email; } public String getUsername() { return username; } public void setUsername(final String username) { this.username = username; } public String getLastName() { return lastName; } public void setLastName(final String lastName) { this.lastName = lastName; } @Override public String toString() { return this.getName() +" |" + this.getLastName() +" |" + this.getEmail() +" |" + this.getUsername() +" |" + this.getId() +" |"; } } |
转弯后呼叫
1 | curl -i -H"Content-Type: application/json" -X POST -d '{"id":100,"username":"JohnBlog","name":"John","lastName":"Blog","email":"[email protected]"}' http://localhost:8080/[YOURWEBAPP]/api/user/add |
相关职位和问题
如果不是所有提供以下文章和问题的人都可以使用此常见问题解答(如果遇到有用的相关文章/问题,此列表将扩展):
转弯后呼叫
1 | curl -i -H"Content-Type: application/json" -X POST -d '{"id":100,"username":"JohnBlog","name":"John","lastName":"Blog","email":"[email protected]"}' http://localhost:8080/[YOURWEBAPP]/api/user/add |
不同的错误方案:
在这里,我将探讨在您发出curl调用后可能遇到的不同错误,以及可能发生的错误。
情景一:1 2 3 4 5 | HTTP/1.1 404 Not Found Server: Apache-Coyote/1.1 Content-Type: text/html;charset=utf-8 Content-Length: 949 Date: Tue, 04 Jun 2013 02:59:35 GMT |
这意味着您提供的URL中不存在RESTAPI。
根本原因:- 你的请求可能有错别字(相信我这是可能的)!
- 可能是您的弹簧配置不正确。如果是这样的话,就需要进一步调查到底出了什么问题,但在开始更复杂的调查之前,我已经提供了一些您需要做的初步行动。
行动:
在确保所有操作都完全正确且配置和URL均无任何错误之后:-把马文干掉。-取消部署您的Web应用程序或直接删除它。-重新部署Web应用程序-确保在Maven/Gradle中只使用一个版本的Spring
情景二:1 2 3 4 5 6 | HTTP/1.1 400 Bad Request Server: Apache-Coyote/1.1 Content-Type: text/html;charset=utf-8 Content-Length: 968 Date: Tue, 04 Jun 2013 03:08:05 GMT Connection: close |
唯一的原因是您的请求格式不正确。如果您签出详细的curl响应,您应该能够看到"客户端发送的请求语法错误"。
根本原因:您的JSON格式不正确,或者缺少Java对象的强制参数。
行动:确保以正确的格式和正确的参数数目提供JSON对象。可空属性不是必需的,但必须为所有不可空属性提供数据。非常重要的是要记住,Spring使用Java反射将JSON文件转换成Java对象,这意味着什么?这意味着变量和方法名称区分大小写。如果您的JSON文件正在发送变量"用户名",那么Java对象中的匹配变量也必须命名为"用户名"。如果你有getter和setter,它们也必须遵循相同的规则。getusername和setusername与前面的示例匹配。
塞纳里奥三号:1 2 3 4 5 | HTTP/1.1 415 Unsupported Media Type Server: Apache-Coyote/1.1 Content-Type: text/html;charset=utf-8 Content-Length: 1051 Date: Wed, 24 Aug 2011 08:50:17 GMT |
根本原因:
Web服务不支持JSON媒体类型。这可能是因为您的注释没有指定媒体类型,或者您没有在curl post命令中指定媒体类型。
行动:检查是否正确设置了消息转换器,并确保Web服务注释与上面的示例匹配。如果可以,请确保在curl post请求中指定内容类型。
Web服务不支持JSON媒体类型。
塞纳里奥(!):1 2 3 4 5 | HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Tue, 04 Jun 2013 03:06:16 GMT |
恭喜用户实际发送到您的服务器端RESTAPI。
有关如何设置弹簧的详细信息,请参阅弹簧MVC指南。
相关职位和问题如果不是所有提供以下文章和问题的人都可以使用此常见问题解答(如果遇到有用的相关文章/问题,此列表将扩展):
应该注意,如果一个bean类的一个字段有2个或多个setter,而可选字段上没有
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @JsonGetter public String getStatus() { return this.status; } @JsonSetter public void setStatus(String status) { this.status = status; } @JsonIgnore public void setStatus(StatusEnum status) { if (status == null) { throw new NullPointerException(); } this.status = status.toString(); } |
更新:在这种情况下,我们还必须指定
刚用Spring3.2.2和Jackson2.2测试过。它作为参数(
更新2:
如果指定