TL:DR
我们如何覆盖Spring 4.3+的当前行为,强制使用RequestMethod.GET或@GetMapping来处理HEAD请求,这样我们就可以返回Content-Length头而不必将所有数据都写入响应OutputStream?
更长的版本:
我刚才注意到Spring已经改变了默认处理GET / HEAD请求的方式:
HTTP HEAD,OPTIONS
@GetMapping - 以及@RequestMapping(method = HttpMethod.GET),支持 HTTP HEAD透明地用于请求映射目的。调节器 方法不需要改变。应用的响应包装器 javax.servlet.http.HttpServlet,确保" Content-Length"标题是 设置为写入的字节数,而不实际写入 响应。
@GetMapping - 以及@RequestMapping(method = HttpMethod.GET), 隐式映射到并支持HTTP HEAD。 HTTP HEAD请求 处理好像是HTTP GET,但不是写 body,字节数和" Content-Length"头 集。
默认情况下,通过设置"允许"来处理HTTP OPTIONS。响应 标题到所有@RequestMapping中列出的HTTP方法列表 匹配网址格式的方法。
对于没有HTTP方法声明的@RequestMapping,"允许" header设置为" GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS"。调节器 方法应该总是声明支持的HTTP方法,例如 通过使用HTTP方法特定的变体 - @GetMapping, @PostMapping等
@RequestMapping方法可以显式映射到HTTP HEAD和HTTP 选项,但在通常情况下这不是必需的。
来源:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-head-options
https://stackoverflow.com/a/45412434/42962
我们如何覆盖此默认行为,以便我们可以处理HEAD响应并自行设置Content-Length标头?
我们希望这样做是因为我们通过我们的Web应用程序切断大型文件(想想大小超过10个gig),如果可能的话,我们不必将所有字节读入Response的OutputStream。
以下是我们当前代码的示例。只调用第二个方法(带RequestMethod.GET的handleRequest)。
@RequestMapping(value = "/file/{fileName:.+}", method = RequestMethod.HEAD)
public void handleHeadRequest(@RequestParam(value = "fileName") String fileName, HttpServletRequest request, HttpServletResponse response) {
File file = fileRepository.getFileByName(fileName)
response.addHeader("Accept-Ranges", "bytes");
response.addDateHeader("Last-Modified", file.lastModified());
Long fileSize = file.length();
response.addHeader(HttpHeaderConstants.CONTENT_LENGTH, fileSize.toString());
}
@RequestMapping(value = "/file/{fileName:.+}", headers = "!Range", method = RequestMethod.GET)
public void handleRequest(@PathVariable(value = "fileName") String fileName, HttpServletRequest request, HttpServletResponse response) throws Exception {
File file = fileRepository.getFileByName(fileName)
response.addHeader("Accept-Ranges", "bytes");
response.addDateHeader("Last-Modified", file.lastModified());
Long fileSize = file.length();
response.addHeader(HttpHeaderConstants.CONTENT_LENGTH, fileSize.toString());
// Stream file to end user client.
fileDownloadHandler.handle(request, response, file);
}
答案 0 :(得分:0)
HTTP HEAD方法请求返回的标头 将使用HTTP GET方法请求指定的资源。这样的 请求可以在决定下载大型资源之前完成 例如,节省带宽。
对HEAD方法的响应不应该有正文。如果是这样,那一定是 忽略。即便如此,描述身体内容的实体标题, 例如Content-Length可以包含在响应中。它们没有联系 HEAD响应的主体应该是空的,但是应该是 使用GET方法的类似请求的主体将返回为 响应。
如果HEAD请求的结果显示a之后的缓存资源 GET请求现在已过时,即使没有GET,缓存也会失效 请求已经提出。
隐含的HEAD支持 来自Spring的MVC doc:
映射到“GET”的@RequestMapping方法也隐式映射到 “HEAD”,即不需要明确声明“HEAD”。一个 处理HTTP HEAD请求就像它是HTTP GET一样 而不是写身体只计算字节数和 “Content-Length”标题集。
检查点: 这意味着我们永远不必单独为HTTP HEAD动词创建一个处理程序方法,因为Spring已经为目标URL定义了GET动词,因为它隐式支持它。
示例强>
控制器
让我们创建一个非常简单的控制器,并使用一个处理程序方法填充一些标题:
@Controller
public class MyController {
Logger logger = Logger.getLogger(MyController.class.getSimpleName());
@RequestMapping(value = "test", method = {RequestMethod.GET})
public HttpEntity<String> handleTestRequest () {
MultiValueMap<String, String> headers = new HttpHeaders();
headers.put("test-header", Arrays.asList("test-header-value"));
HttpEntity<String> responseEntity = new HttpEntity<>("test body", headers);
logger.info("handler finished");
return responseEntity;
}
}
JUnit测试
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = MyWebConfig.class)
public class ControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setup () {
DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.wac);
this.mockMvc = builder.build();
}
@Test
public void testGet () throws Exception {
MockHttpServletRequestBuilder builder =
MockMvcRequestBuilders.get("/test");
this.mockMvc.perform(builder)
.andExpect(MockMvcResultMatchers.status()
.isOk())
.andDo(MockMvcResultHandlers.print());
}
@Test
public void testHead () throws Exception {
MockHttpServletRequestBuilder builder =
MockMvcRequestBuilders.head("/test");
this.mockMvc.perform(builder)
.andExpect(MockMvcResultMatchers.status()
.isOk())
.andDo(MockMvcResultHandlers.print());
}
}
隐含OPTIONS支持 来自Spring的MVC doc:
@RequestMapping方法内置了对HTTP OPTIONS的支持。通过 默认情况下,通过设置“允许”来处理HTTP OPTIONS请求 对所有显式声明的HTTP方法的响应标头 具有匹配URL模式的@RequestMapping方法。什么时候没有HTTP 方法被显式声明为“Allow”标头设置为 “GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS”
检查点: 这意味着,我们永远不必单独为HTTP OPTIONS动词创建一个处理程序方法,因为Spring隐式支持它,因为所有处理程序方法都明确指定HTTP方法@RequestMapping为目标网址。
示例强>
让我们继续上面的例子,再为HTTP OPTIONS动词添加一个测试:
@Test
public void testOptions () throws Exception {
ResultMatcher accessHeader = MockMvcResultMatchers.header()
.string("Allow", "GET,HEAD");
MockHttpServletRequestBuilder builder =
MockMvcRequestBuilders.options("/test");
this.mockMvc.perform(builder)
.andExpect(MockMvcResultMatchers.status()
.isOk())
.andExpect(accessHeader)
.andDo(MockMvcResultHandlers.print());
}