天天看点

Springboot 之 HandlerMethodArgumentResolver 运用

作者:BUG弄潮儿

介绍

在项目中,如果需要在 Header 中获取请求头,一般使用 RequestHeader 注解。代码案例如下:

@RequestMapping("/normalHeaders")
 public Map<String, Object> normalHeaders(@RequestHeader("user-id")Long userId,
            @RequestHeader("tenant-id")Long tenantId,
            @RequestHeader("user-name")String userName){
  Map<String, Object> map = new HashMap<>();
  map.put("userId", userId);
  map.put("tenantId", tenantId);
  map.put("userName", userName);
  return map;
 }
           

请求curl

curl -X POST \
  http://127.0.0.1:8080/normalHeaders \
  -H 'tenant-id: 12' \
  -H 'user-id: 1' \
  -H 'user-name: buger'
           

使用 RequestHeader 注解获取请求头,如果获取一两个到不会写很多重复代码,但是如果需要获取很多个请求时,代码会变得重复。 下面介绍一种新的解决方案;不但减少了很多重复的代码,而且使得代码变得更简洁。

pom.xml 文件引入依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>com.olive</groupId>
 <artifactId>springmvc-headers</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>springmvc-headers</name>
 <url>http://maven.apache.org</url>

 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.5.14</version>
  <relativePath /> <!-- lookup parent from repository -->
 </parent>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <maven.compiler.source>8</maven.compiler.source>
  <maven.compiler.target>8</maven.compiler.target>
 </properties>

 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
  </dependency>
  <dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
  </dependency>
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
 </dependencies>
</project>
           

解析请求头,并包装

实现 HandlerMethodArgumentResolver 类;解析请求头,包装成 HeadersWrapperDTO 类

package com.olive.config;


import com.olive.dto.HeadersWrapperDTO;
import org.springframework.core.MethodParameter;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

public class RequestHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().isAssignableFrom(HeadersWrapperDTO.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest,
                                  WebDataBinderFactory binderFactory) throws Exception {
        String userId = webRequest.getHeader("user-id");
        String tenantId = webRequest.getHeader("tenant-id");
        String userName = webRequest.getHeader("user-name");

        HeadersWrapperDTO headersWrapperDTO = new HeadersWrapperDTO();
        if(StringUtils.hasText(userId)){
            headersWrapperDTO.setUserId(Long.parseLong(userId));
        }
        if(StringUtils.hasText(tenantId)){
            headersWrapperDTO.setTenantId(Long.parseLong(tenantId));
        }
        headersWrapperDTO.setUserName(userName);
        return headersWrapperDTO;
    }
}
           

HeadersWrapperDTO POJO类

package com.olive.dto;

import lombok.Data;

import java.io.Serializable;

@Data
public class HeadersWrapperDTO implements Serializable {

    private Long userId;

    private Long tenantId;

    private String userName;

}
           

注册 RequestHandlerMethodArgumentResolver 到 Controller 参数解析器里,即添加自己的参数解析器

package com.olive.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class MethodArgumentResolverConfig {

    @Bean
    public WebMvcConfigurer getWebMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
                resolvers.add(new RequestHandlerMethodArgumentResolver());
            }
        };
    }
}
           

测试

编码 Springboot 启动引导类

package com.olive;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }

}
           

编写测试 Controller

package com.olive.controller;

import java.util.HashMap;
import java.util.Map;

import com.olive.dto.HeadersWrapperDTO;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
 
 @RequestMapping("/normalHeaders")
 public Map<String, Object> normalHeaders(@RequestHeader("user-id")Long userId,
            @RequestHeader("tenant-id")Long tenantId,
            @RequestHeader("user-name")String userName){
  Map<String, Object> map = new HashMap<>();
  map.put("userId", userId);
  map.put("tenantId", tenantId);
  map.put("userName", userName);
  return map;
 }

 @RequestMapping("/wrapperHeaders")
 public Map<String, Object> wrapperHeaders(HeadersWrapperDTO headers){
  Map<String, Object> map = new HashMap<>();
  map.put("userId", headers.getUserId());
  map.put("tenantId", headers.getTenantId());
  map.put("userName", headers.getUserName());
  return map;
 }

}
           

测试curl

curl -X POST \
  http://127.0.0.1:8080/wrapperHeaders \
  -H 'tenant-id: 12' \
  -H 'user-id: 1' \
  -H 'user-name: buger'
           

通过 RequestHandlerMethodArgumentResolver 可以对请求头进行解析并封装到 HeadersWrapperDTO 类中,这样减少了在 Controller 使用大量的 RequestHeader 注解获取请求头。