- Introduction
- Quick Start
- Design
- Tutorial
- Spring MVC
- JSP/JSTL/Servlet
- JQuery and plugins
- Ajax
- Twitter Bootstrap CSS
- SiteMesh
- YUICompressor
- Spring Restful Service
- JAX-WS/CXF
- Spring Data JPA
- JPA/Hibernate
- MyBatis
- Database
- H2 Database
- Datasource
- Transaction
- Redis
- Cache Overview
- Guava Cache
- Ehcache
- Memcached
- Shiro Security
- Crypto
- Schedule/Quartz
- Jmx
- System Protection
- Hystrix
- Rate Limiter
- Monitoring and Metrics
- Metrics-library
- Graphite
- Logging/Slf4j/Logback
- Logstash
- Spring
- Validation Overview
- JQuery Validation
- Hibernate Validator
- General Utilizes
- JSON/JAXB
- Date
- Test Overview
- Unit Test/Mockito/AssertJ
- Selenium2
- BDD
- Performace-test
- JMeter
- Profiler
- Simulator Overview
- Nodejs
- Eclipse
- Maven
- Sonar
- git
- Travis CI
- Jetty
- Micro-Service Architecture/Executable War
- AssertJ
- CreateNewProject
- Dozer
- Graphite-Windows
- Hello-Everybody
- Jaxb
- Json
- Mock
Spring Restful Service
1. Overview
JAX-RS协议的参考实现 Jersey 无论服务端还是客户端都是不错的,SpringSide之前也一直用它。 从RC4版后为了减少技术的使用,才用SpringMVC + RestTemplate进行替代。
2. JAX-RS
JAX-RS毕竟是个规范,在某些场合使用也非常合适,详见 Jersey ,之前的使用心得. 在Showcase中,顺道用CXF作为JAX-RS框架进行了演示,见AccountJaxRsService.java 和 applicationContext-jaxrs-server.xml.
3. ServerSide: Spring MVC
Spring MVC虽然没有实现JAX-RS协议,但整体风格与annotation和JAX-RS都已经非常接近了。
3.1 Example
在Quickstart中演示了一个典型的CRUD的Restful Service,演示了各种返回各种状态码(404), 返回新创建对象的URL的方式。
@RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<?> get(@PathVariable("id") Long id) {
Task task = taskService.getTask(id);
if (task == null) {
return new ResponseEntity(HttpStatus.NOT_FOUND);
}
return new ResponseEntity(task, HttpStatus.OK);
}
3.2 Content negotiation, 根据url后缀返回JSON与XML两种风格
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="mediaTypes" >
<value>
json=application/json
xml=application/xml
</value>
</property>
</bean>
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@ResponseBody
public UserDTO getUser(@PathVariable("id") Long id) {
}
Content negotiation的最大好处是,可以定义函数只返回Java对象,然后Spring MVC会根据URL后缀自动判定Content-Type及渲染成相应的View,如/api/v1/user/${id},测试用例见showcase中的UserRestFt。 当然,现在既要返回JSON,又要返回XML的接口越来越少了,多数只专心于JSON。
3.3 异常控制
详见SpringMVC的相关章节,为了避免扰乱别人,Rest专门有自己的Exception。
3.4 MeidaType
4. ClientSide: Spring RestTemplate
RestTemplate胜在够简单,详见Spring官方手册,通过getForObject()/getForEntity(), postForLocation()/postForObject()等方法快速实现访问URL并将结果转换为Object。 在quickstart的functional test 中TaskRestFT.java中,完整演示了其使用。
4.1 转换类型为List<?>的返回值
如果服务端返回的是一个JSON格式编码的集合时,最快的转换方法是定义一个List的子类,然后作为Class参数.
private static class TaskList extends ArrayList<Task> {
};
TaskList tasks = restTemplate.getForObject(resoureUrl, TaskList.class);
4.2 处理Header
如果要在Request/Response中处理Header,则比 Jersey 要复杂一点,需要用到比较原始的exchange()方法,见showcase中的UserRestFT.java.
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set(com.google.common.net.HttpHeaders.AUTHORIZATION, Servlets.encodeHttpBasic("admin", "admin"));
HttpEntity<?> requestEntity = new HttpEntity(requestHeaders);
HttpEntity<UserDTO> response = restTemplate.exchange(resoureUrl + "/{id}.xml", HttpMethod.GET, requestEntity,UserDTO.class, 1L);
如果有很多函数都有相同的Header处理请求, 又不想用exchange()这么冗长的写法,可以使用ClientHttpRequestInterceptor, 同样在showcase中进行了演示, 下面的代码与上面的例子的效果是一样的。
ClientHttpRequestInterceptor interceptor = new HttpBasicInterceptor("admin", "admin");
restTemplate.setInterceptors(Lists.newArrayList(interceptor));
restTemplate.getForObject(resoureUrl + "/{id}.json", UserDTO.class, 1L);
4.3 底层Http Connection管理及超时控制
RestTemplate 默认使用SimpleClientHttpRequestFactory, 基于JDK自带的HttpURL Connection. 也可以设置基于Apache HttpClient4.0,并使用了多线程安全的Connection Pool的HttpComponentsClientHttpRequestFactory.(此时注意在退出时要调用其destroy()方法) 无论哪种RequestFactory,都可以调用setConnectTimeout()/setReadTimeout()方法设置超时,以毫秒为单位,0是无超时控制,不设的话则是系统默认值。
同样在showcase的UserRestFt中进行了演示。
5. XML与JSON格式的转换
无论Server还是client,Spring已自带了一堆Converter:
- MappingJackson2HttpMessageConverter,负责用Jackson2.0转换JSON格式数据
- Jaxb2RootElementHttpMessageConverter, 负责用Jaxb2RootElementHttpMessageConverter转换XML格式数据,注意DTO必须用@XmlRootElement标注才会被转换,另外如果DTO之间循环依赖,会抛出Spring MVC会抛出406的返回码,需要在它的writeToResult()方法上设置断点来调试,才能看到真正原因。
如果自定义Converters,在server-side:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="com.google.protobuf.spring.http.ProtobufHttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
在client-side,调用restTemplate的setMessageConverters()函数:
restTemplate.setMessageConverters(Arrays.asList(new MappingJackson2HttpMessageConverter())
6. 安全认证
Restful API一般不搞Login/Logout+SessionId的认证方式,而是无状态的每次附带认证信息。 Showcase中演示了Http Basic在Http Header中明文传密码,然后利用Shiro的http basic集成。 当然,也可以自己简单的写个Filter来做这个事情。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论