天天看點

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 注解擷取請求頭。