Spring ResponseEntity
简单记录下 ResponseEntity 的使用方式
@RequestMapping(value="/demo1" ) public ResponseEntity demo1(){ // 使用方式一. // ResponseEntity responseEntity = new ResponseEntity(new User("lvbb",24),HttpStatus.OK); // 使用方式二. return ResponseEntity.ok(new User("lvbb",24)); }
效果: 在引入jackson的jar包,以及开启<mvc:annotation-driven/>之后,访问请求可以看到 界面上显示出 User 的json格式。
说明. ResponseEntity可以理解为 @ResponseBody + @ResponseStatus 的组合.
ResponseEntity类介绍:
ResponseEntity类继承自HttpEntity,有三个关键属性 httpStatus 、body、httpHeader,分别代表响应状态码、响应体、响应头信息;
原理简单记录:
Spring对于@RequestMapping方法返回值有个接口,专门用来处理返回值类型HandlerMethodReturnValueHandler,接口两个声明方法:supportsReturnType判断是否支持对当前返回值类型,如果是支持解析该种返回值,就调用接口第二个方法handleReturnValue,解析@RequestMapping返回值。
Spring4.3.0 <mvc:annotation-driven/>一共会为我们注册15个HandlerMethodReturnValueHandler的实现类. 其中 RequestResponseBodyMethodProcessor用来处理@ResponseBody,而HttpEntityMethodProcessor是用来处理 ReponseEntity类型的返回值. 这里也可以发现一点:HttpEntityMethodProcessor在arrayList中的位置要比RequestResponseBodyMethodProcessor靠前,所以 使用了 ResponseEntity 就没有@ResponseBody出场的机会了.
从HandlerMethodReturnValueHandler接口的第一个方法分析使用满足条件:
代码片段位于:org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor#supportsReturnType
只要@RequestMapping的方法返回值为 HttpEntity的实现类 且不是 RequestEntity类或其子类 就会使用HttpEntityMethodProcessor来处理 请求返回值,简单来说ResponseEntity类就行.
从HandlerMethodReturnValueHandler接口的第二个方法分析使用如何将ResponseEntity返回给浏览器?
代码片段位于:org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor#handleReturnValue
对比@ResponseEntity解析器RequestResponseBodyMethodProcessor,很多地方相似,这两个解析器都继承自AbstractMessageConverterMethodProcessor。
记录下与@ResponseBody不同的地方:1. 可以设置HttpHeaders响应头消息,并通过Response写回;通过这种方式 ResponseEntity可以实现下载文件
2. 可以设置HttpStatus,设置响应状态码.
代码具体地方都和ResponseEntity一样,可以看下我这篇解析:
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { mavContainer.setRequestHandled(true); if (returnValue == null) { return; } ServletServerHttpRequest inputMessage = createInputMessage(webRequest); ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); Assert.isInstanceOf(HttpEntity.class, returnValue); HttpEntity<?> responseEntity = (HttpEntity<?>) returnValue; HttpHeaders outputHeaders = outputMessage.getHeaders(); HttpHeaders entityHeaders = responseEntity.getHeaders(); if (outputHeaders.containsKey(HttpHeaders.VARY) && entityHeaders.containsKey(HttpHeaders.VARY)) { List<String> values = getVaryRequestHeadersToAdd(outputHeaders, entityHeaders); if (!values.isEmpty()) { outputHeaders.setVary(values); } } if (!entityHeaders.isEmpty()) { for (Map.Entry<String, List<String>> entry : entityHeaders.entrySet()) { if (!outputHeaders.containsKey(entry.getKey())) { outputHeaders.put(entry.getKey(), entry.getValue()); } } } if (responseEntity instanceof ResponseEntity) { outputMessage.getServletResponse().setStatus(((ResponseEntity<?>) responseEntity).getStatusCodeValue()); HttpMethod method = inputMessage.getMethod(); boolean isGetOrHead = (HttpMethod.GET == method || HttpMethod.HEAD == method); if (isGetOrHead && isResourceNotModified(inputMessage, outputMessage)) { outputMessage.setStatusCode(HttpStatus.NOT_MODIFIED); // Ensure headers are flushed, no body should be written. outputMessage.flush(); // Skip call to converters, as they may update the body. return; } } // Try even with null body. ResponseBodyAdvice could get involved. writeWithMessageConverters(responseEntity.getBody(), returnType, inputMessage, outputMessage); // Ensure headers are flushed even if no body was written. outputMessage.flush(); }
简单记录下:ResponseEntity小文件下载的方式
@RequestMapping(value="/demo2") public ResponseEntity demo2() throws IOException { ClassPathResource resource = new ClassPathResource("download/note.txt"); InputStream in = resource.getInputStream(); byte[] bytes = new byte[in.available()]; in.read(bytes); HttpHeaders headers=new HttpHeaders(); headers.add("Content-Disposition","attachment;filename="+resource.getFilename()); HttpStatus statusCode=HttpStatus.OK; return new ResponseEntity(bytes,headers,statusCode); }
效果等同于:利用@ResponseEntity返回byte数组
//通过 ResponseBody返回文件 二进制文件 @RequestMapping(value="/demo11") @ResponseBody public byte[] demo11(HttpServletResponse response) throws IOException { ClassPathResource resource = new ClassPathResource("download/note.txt"); InputStream in = resource.getInputStream(); byte[] bytes = new byte[in.available()]; in.read(bytes); response.addHeader("Content-Disposition","attachment;filename="+resource.getFilename()); return bytes; }
最原始的方式:
@RequestMapping(value="/demo3") public void demo3(HttpServletResponse response) throws IOException { ClassPathResource resource = new ClassPathResource("download/note.txt"); InputStream in = resource.getInputStream(); response.addHeader("Content-Disposition","attachment;filename="+resource.getFilename()); FileCopyUtils.copy(in,response.getOutputStream()); }