从请求中提取自定义标头

2023/05/12

1. 概述

在这个简短的教程中,我们将探讨为Spring应用程序提取请求标头的各种方法。我们将学习如何针对特定端点执行此操作,之后,我们将创建一个HandlerInterceptor来拦截所有传入请求并提取标头。

2. 使用HttpServletRequest

为了能够访问有关HTTP请求的信息,我们可以声明一个HttpServletRequest对象作为我们端点的参数。这允许我们访问请求详细信息,例如路径、查询参数、cookies和headers。

例如,我们可以使用HttpServletRequest在收到请求时提取自定义标头。要访问某个标头,我们可以通过指定标头的key来使用getHeader()方法:

@RestController
public class FooBarController {

    @GetMapping("foo")
    public String foo(HttpServletRequest request) {
        String operator = request.getHeader("operator");
        return "hello, " + operator;
    }
}

我们可以使用MockMvc发送包含自定义标头的GET请求。如果我们将operator标头设置为“John.Doe”,我们将期望响应为“hello, John.Doe”:

@Test
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
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中,并将其注入到可能需要的不同组件中。

首先,让我们将operator名称包装到一个对象中:

public class OperatorHolder {
    private String operator;
    // getter and setter
}

现在,让我们使用@Bean将其声明为一个bean。operator可能因请求而异,因此我们应该将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
}

结果,请求被拦截,operator标头被提取,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());
    }
}

现在要访问operator,我们只需要注入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上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章