1. 概述
在这个简短的教程中,我们将探讨在Spring应用程序中提取请求头的各种方法。我们将学习如何针对特定端点操作,并随后创建一个HandlerInterceptor
,它拦截所有入站请求并提取头部信息。
2. 使用HttpServletRequest
为了能够访问HTTP请求的信息,我们可以在端点的参数中声明一个HttpServletRequest
对象。这样,我们可以查看路径、查询参数、cookies和头部等详细内容。
例如,当我们接收到请求时,可以使用HttpServletRequest
来提取自定义头部。要获取特定头部,我们可以使用getHeader()
方法,指定头部的键:
@RestController
public class FooBarController {
@GetMapping("foo")
public String foo(HttpServletRequest request) {
String operator = request.getHeader("operator");
return "hello, " + operator;
}
}
我们可以使用MockMvc发送一个包含自定义头部的GET请求。如果我们将操作员头部设置为"John.Doe"
,则期望响应为"hello, John.Doe"
:
@Test
public void givenARequestWithOperatorHeader_whenWeCallFooEndpoint_thenOperatorIsExtracted() throws Exception {
MockHttpServletResponse response = this.mockMvc.perform(get("/foo").header("operator", "John.Doe"))
.andDo(print())
.andReturn()
.getResponse();
assertThat(response.getContentAsString()).isEqualTo("hello, John.Doe");
}
然而,如果我们只需要请求中的一个特定头部,将整个HttpServletRequest
作为参数可能会违反接口分离原则,即SOLID设计模式中的“I”。
3. 使用@RequestHeader
对于特定端点访问头部的另一种简单方式是使用@RequestHeader
注解:
@GetMapping("bar")
public String bar(@RequestHeader("operator") String operator) {
return "hello, " + operator;
}
因此,我们的代码不再与整个HttpServletRequest
对象耦合,现在我们的方法使用作为参数传递的所有数据。
让我们为这个端点编写类似的测试,期望得到相同的结果:
@Test
public void givenARequestWithOperatorHeader_whenWeCallBarEndpoint_thenOperatorIsExtracted() throws Exception {
MockHttpServletResponse response = this.mockMvc.perform(get("/bar").header("operator", "John.Doe"))
.andDo(print())
.andReturn()
.getResponse();
assertThat(response.getContentAsString()).isEqualTo("hello, John.Doe");
}
4. 使用HandlerInterceptor
对于更复杂的用例,我们可以使用HandlerInterceptor
对象。优点是它可以拦截所有入站请求,并提取头部的值。
此外,我们可以将头部值包装成一个具有request
作用域的Spring Bean,并将其注入到可能需要它的不同组件中。
首先,我们将操作员名称封装到一个对象中:
public class OperatorHolder {
private String operator;
// getter and setter
}
然后,我们使用@Bean
声明它。操作员可能因请求而异,因此我们应该将bean的作用域设置为SCOPE_REQUEST
:
@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public OperatorHolder operatorHolder() {
return new OperatorHolder();
}
接下来,我们需要创建一个自定义的HandlerInterceptor
实现,并重写preHandle()
方法:
public class OperatorInterceptor implements HandlerInterceptor {
private final OperatorHolder operatorHolder;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String operator = request.getHeader("operator");
operatorHolder.setOperator(operator);
return true;
}
// constructor
}
这样,请求被拦截,操作员头部被提取,并且OperatorHolder
Bean被更新。
最后,我们需要将自定义拦截器添加到Spring MVC的InterceptorRegistry
中。我们可以通过实现WebMvcConfigurer
并重写addInterceptor()
方法来做到这一点:
@Configuration
public class HeaderInterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(operatorInterceptor());
}
@Bean
public OperatorInterceptor operatorInterceptor() {
return new OperatorInterceptor(operatorHolder());
}
}
现在要访问操作员,只需注入OperatorHolder
Bean并调用getOperator()
方法即可:
@RestController
public class BuzzController {
private final OperatorHolder operatorHolder;
@GetMapping("buzz")
public String buzz() {
return "hello, " + operatorHolder.getOperator();
}
// constructor
}
5. 总结
在这篇文章中,我们探讨了为入站HTTP请求提取自定义头部的不同方法。
首先,我们了解了如何通过HttpServletRequest
和@RequestHeader
为特定端点操作。然后,我们看到了HandlerInterceptor
如何帮助我们从所有入站请求中提取头部,提供了一个更通用的解决方案。
如往常一样,教程的完整源代码可在GitHub上找到:https://github.com/eugenp/tutorials/tree/master/spring-boot-modules/spring-boot-mvc-5。