发布网友 发布时间:2022-04-21 19:55
共1个回答
热心网友 时间:2022-04-26 11:47
Spring MVC本身对Restful支持非常好。它的@RequestMapping、@RequestParam、@PathVariable、@ResponseBody注解很好的支持了REST。18.2 Creating RESTful services
1. @RequestMapping
Spring uses the @RequestMapping method annotation to define the URI Template for the request. 类似于struts的action-mapping。 可以指定POST或者GET。
2. @PathVariable
The @PathVariable method parameter annotation is used to indicate that a method parameter should be bound to the value of a URI template variable. 用于抽取URL中的信息作为参数。(注意,不包括请求字符串,那是@RequestParam做的事情。)
@RequestMapping("/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable String ownerId, Model model) {
// ...
}
如果变量名与pathVariable名不一致,那么需要指定:
@RequestMapping("/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable("ownerId") String theOwner, Model model) {
// implementation omitted
}
Tip
method parameters that are decorated with the @PathVariable annotation can be of any simple type such as int, long, Date... Spring automatically converts to the appropriate type and throws a TypeMismatchException if the type is not correct.
3. @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";
}
还可以设置defaultValue:
@RequestMapping("/imageUpload")
public String processImageUpload(@RequestParam(value="name", defaultValue="arganzheng") 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";
}
4. @RequestBody和@ResponseBody
这两个注解其实用到了Spring的一个非常灵活的设计——HttpMessageConverter 18.3.2 HTTP Message Conversion
与@RequestParam不同,@RequestBody和@ResponseBody是针对整个HTTP请求或者返回消息的。前者只是针对HTTP请求消息中的一个 name=value 键值对(名称很贴切)。
HtppMessageConverter负责将HTTP请求消息(HTTP request message)转化为对象,或者将对象转化为HTTP响应体(HTTP response body)。
public interface HttpMessageConverter<T> {
// Indicate whether the given class is supported by this converter.
boolean supports(Class<? extends T> clazz);
// Return the list of MediaType objects supported by this converter.
List<MediaType> getSupportedMediaTypes();
// Read an object of the given type form the given input message, and returns it.
T read(Class<T> clazz, HttpInputMessage inputMessage) throws IOException,
HttpMessageNotReadableException;
// Write an given object to the given output message.
void write(T t, HttpOutputMessage outputMessage) throws IOException,
HttpMessageNotWritableException;
}
Spring MVC对HttpMessageConverter有多种默认实现,基本上不需要自己再自定义HttpMessageConverter
StringHttpMessageConverter - converts strings
FormHttpMessageConverter - converts form data to/from a MultiValueMap<String, String>
ByteArrayMessageConverter - converts byte arrays
SourceHttpMessageConverter - convert to/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:
public class AnnotationMethodHandlerAdapter extends WebContentGenerator
implements HandlerAdapter, Ordered, BeanFactoryAware {
...
public AnnotationMethodHandlerAdapter() {
// no restriction of HTTP methods by default
super(false);
// See SPR-7316
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setWriteAcceptCharset(false);
this.messageConverters = new HttpMessageConverter[]{new ByteArrayHttpMessageConverter(), stringHttpMessageConverter,
new SourceHttpMessageConverter(), new XmlAwareFormHttpMessageConverter()};
}
}
如上:默认的HttpMessageConverter是ByteArrayHttpMessageConverter、stringHttpMessageConverter、SourceHttpMessageConverter和XmlAwareFormHttpMessageConverter转换器。所以需要配置一下:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=GBK</value>
</list>
</property>
</bean>
<bean class="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);
}
一般情况下我们是不需要自定义HttpMessageConverter,不过对于Restful应用,有时候我们需要返回jsonp数据:
package me.arganzheng.study.springmvc.util;
import java.io.IOException;
import java.io.PrintStream;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
public class MappingJsonpHttpMessageConverter extends MappingJacksonHttpMessageConverter {
public MappingJsonpHttpMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationConfig(objectMapper.getSerializationConfig().withSerializationInclusion(Inclusion.NON_NULL));
setObjectMapper(objectMapper);
}
@Override
protected void writeInternal(Object o, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
String jsonpCallback = null;
RequestAttributes reqAttrs = RequestContextHolder.currentRequestAttributes();
if(reqAttrs instanceof ServletRequestAttributes){
jsonpCallback = ((ServletRequestAttributes)reqAttrs).getRequest().getParameter("jsonpCallback");
}
if(jsonpCallback != null){
new PrintStream(outputMessage.getBody()).print(jsonpCallback + "(");
}
super.writeInternal(o, outputMessage);
if(jsonpCallback != null){
new PrintStream(outputMessage.getBody()).println(");");
}
}
}