Spring MV SpringMVC对restful的支持
1. C中的常用注解
(1)@Controller
对将成为 MVC 中控制器的类进行注解并处理HTTP 请求。
(2)@RequestMapping
对函数进行注解,该函数处理某些 HTTP 方法、URI 或 HTTP 头。
此注解是 Spring REST 支持的关键。可以更改 method 参数以处理其他 HTTP 方法。
例如:
@RequestMapping(method=RequestMethod.GET,value="/emps",
headers="Accept=application/xml,application/json")
(3)@PathVariable
可将 URI 中的路径变量作为参数插入。
@RequestMapping("/owners/{ownerId}",method=RequestMethod.GET)
public String findOwner(@PathVariableString ownerId, Model model) {
// ...
}
(4)@RequestParam
这个注解跟@PathVariable功能差不多,只是参数值的来源不一样而已。它的取值来源是请求参数(querystring或者post表单字段)
对了,因为它的来源可以是POST字段,所以它支持更丰富和复杂的类型信息。比如文件对象:
@RequestMapping("/imageUpload")
public String processImageUpload(
@RequestParam("name") String name,
@RequestParam("description")String description,
@RequestParam("image")MultipartFile image) throws IOException {
this.imageDatabase.storeImage(name, image.getInputStream(),
(int) image.getSize(), description);
return "redirect:imageList";
}
(5)@RequestHeader
将某一 HTTP 头插入方法中
(6)@RequestBody
将 HTTP 请求正文插入方法中
(7)@ResponseBody
将内容或对象作为 HTTP响应正文返回。
(8)HttpEntity<T>
如果将它作为参数提供,自动插入方法中
(9)ResponseEntity<T>
返回具有自定义状态或头的HTTP 响应
例如:
public @ResponseBody EmployeegetEmployeeBy(
@RequestParam("name")Stringname,
@RequestHeader("Accept")String accept,
@RequestBody String body) {…}
public ResponseEntity<String> method(HttpEntity<String> entity) {…}
1.1 @RequestBody和@ResponseBody
这两个注解其实用到了Spring的一个非常灵活的设计——HttpMessageConverter 18.3.2 HTTPMessage Conversion
与@RequestParam不同,@RequestBody和@ResponseBody是针对整个HTTP请求或者返回消息的。前者只是针对HTTP请求消息中的一个 name=value 键值对(名称很贴切)。
HtppMessageConverter负责将HTTP请求消息(HTTPrequest message)转化为对象,或者将对象转化为HTTP响应体(HTTP response body)。
public interface HttpMessageConverter<T> {
// Indicatewhether the given class is supported by this converter.
booleansupports(Class<? extends T> clazz);
// Return thelist of MediaType objects supported by this converter.
List<MediaType> getSupportedMediaTypes();
// Read anobject of the given type form the given input message, and returns it.
Tread(Class<T> clazz, HttpInputMessage inputMessage) throws IOException,
HttpMessageNotReadableException;
// Write angiven object to the given output message.
void write(T t,HttpOutputMessage outputMessage) throws IOException,
HttpMessageNotWritableException;
}
Spring MVC对HttpMessageConverter有多种默认实现,基本上不需要自己再自定义HttpMessageConverter
· StringHttpMessageConverter -converts strings
· FormHttpMessageConverter - convertsform data to/from a MultiValueMap<String, String>
· ByteArrayMessageConverter - convertsbyte arrays
· SourceHttpMessageConverter - convertto/from a javax.xml.transform.Source
· RssChannelHttpMessageConverter -convert to/from RSS feeds
· MappingJacksonHttpMessageConverter -convert to/from JSON using Jackson's ObjectMapper
· etc...
然而对于RESTful应用,用的最多的当然是MappingJacksonHttpMessageConverter。
但是MappingJacksonHttpMessageConverter不是默认的HttpMessageConverter:
可以配置如下:
<beanclass="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<propertyname="messageConverters">
<list>
<beanclass="org.springframework.http.converter.StringHttpMessageConverter">
<propertyname="supportedMediaTypes">
<list>
<value>text/plain;charset=GBK</value>
</list>
</property>
</bean>
<beanclass="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
</list>
</property>
</bean>
配置好了之后,就可以享受@Requestbody和@ResponseBody对JONS转换的便利之处了:
@RequestMapping(value = "api", method = RequestMethod.POST)
@ResponseBody
public boolean addApi(@RequestBody
Api api, @RequestParam(value = "afterApiId", required = false)
Integer afterApiId) {
Integer id = apiMetadataService.addApi(api);
return id > 0;
}
@RequestMapping(value = "api/{apiId}", method = RequestMethod.GET)
@ResponseBody
public Api getApi(@PathVariable("apiId")
int apiId) {
return apiMetadataService.getApi(apiId, Version.primary);
}
2. 使用Spring中RestTemplate 对restful服务进行通信测试
RestTemplate支持其他方法,包括:
exchange:使用请求正文执行一些HTTP 方法并获得响应。
getForObject:执行 HTTP GET 方法并将响应作为对象获得。
postForObject:使用特定请求正文执行HTTP POST 方法。
put:使用特定请求正文执行HTTP PUT 方法。
delete:执行 HTTPDELETE方法以获得特定 URI。
代码示例
(1)显示如何将头添加到请求中,然后调用请求。
使用MarshallingHttpMessageConverter 您可以获得响应并将其转换为类型类。
清单1. XML 具象请求
HttpHeadersheaders = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
HttpEntity<String>entity = new HttpEntity<String>(headers);
ResponseEntity<EmployeeList>response = restTemplate.exchange(
"http://localhost:8080/rest/service/emps",HttpMethod.GET, entity, EmployeeList.class);
EmployeeListingemployees= response.getBody();
// handle theemployees
(2)POST请求
显示了如何将新员工发布到服务器。服务器端服务 addEmp() 可接受媒体类型为 application/xml 和 application/json 的数据。
清单 2. 发布新员工
Employee newEmp= new Employee(99, "guest", "guest@ibm.com");
HttpEntity<Employee>entity = new HttpEntity<Employee>(newEmp);
ResponseEntity<Employee>response = restTemplate.postForEntity(
"http://localhost:8080/rest/service/emp",entity, Employee.class);
Employee e =response.getBody();
// handle theemployee
(3)PUT请求
显示了如何 PUT 修改的员工以更新旧员工。它还显示了可用作请求 URI 占位符({id})的功能
清单3.PUT 以更新员工
Employee newEmp= new Employee(99, "guest99", "guest99@ibm.com");
HttpEntity<Employee>entity = new HttpEntity<Employee>(newEmp);
restTemplate.put("http://localhost:8080/rest/service/emp/{id}",entity, "99");
(4)DELETE请求
显示了如何 DELETE 现有员工。
清单4.DELETE 现有员工
restTemplate.delete("http://localhost:8080/rest/service/emp/{id}","99");
3. 支持RESTful的URL
在开发功能模块之前,应该先把URL设计好。比查对 消息 这个资源的操作URL可以这么设计:
http://arganzheng.me/messages/show/123456
http://arganzheng.me/messages/preview/123456
http://arganzheng.me/messages/delete/123456
http://arganzheng.me/messages/new
http://arganzheng.me/message/update
说明:可以看到我们的URL中有动作在里面,这个相对简单点。
事实上纯粹的RESTfulURL是把动作隐含在HTTP头中:GET、PUT、DELETE、POST。
/blog/1 HTTP GET => 得到id = 1的blog
/blog/1 HTTP DELETE => 删除 id = 1的blog
/blog/1 HTTP PUT => 更新id = 1的blog
/blog HTTP POST => 新增BLOG
不过这样对用户编码有要求,。
要支持这种URL,web.xml需要这么配置:
<!-- REST servlet-mapping -->
<servlet-mapping>
<servlet-name>DispatcherServlet<srvlet-name>
<url-pattern>/</url-pattern>
<srvlet-mapping>
但是这样的话有个问题,就是静态文件也被mapping了,会导致找不到资源。Spring提供了一个resources配置项支持静态文件的处理16.14.5Configuring Serving of Resources:
<!-- Forwards requests to the "/" resource to the "welcome" view -->
<mvc:view-controller path="/" view-name="index"/>
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources/ directory -->
<mvc:resources mapping="/resources/**" location="/resources/" />
<!--
注意:配置了
mvc:resources
就必须配置这个选项,否则
handler mapping
都失效了
@see http://stackoverflow.com/questions/7910845/the-handler-mapping-from-the-mvcresource-override-other-mappings-which-defined
-->
<mvc:annotation-driven />
这样所有请求:http://arganzheng.me/resources/**会映射到webapp下的resources目录,而不是找我们的controller处理。
但是有个奇怪的问题,就是配置这个之后,原来动态东西就不能访问到了,提示找不到对应的handler,解决方案是增加一个<mvc:annotation-driven />配置。具体参见Thehandler mapping from the mvc:resource override other mappings which definedwith annotation。
另外,静态的html页面一般不放在resources路面下,而是直接在根目录下,比如:http://arganzheng.me/index.html或者http://arganzheng.me/404.html。所以应该在web.xml中在配置一个url-mapping规则:
<!--
避免被
Spring DispatcherServlet
接管
-->
<servlet-mapping>
<servlet-name>default<srvlet-name>
<url-pattern>*.html</url-pattern>
<srvlet-mapping>