参考资料

  1. 【SpringBoot】HandlerMethodArgumentResolver的简单使用
  2. HandlerMethodArgumentResolver(参数解析器)的作用+使用小案例
  3. HandlerMethodArgumentResolver用于统一获取当前登录用户


目录

  • 一. 需求
  • 二. 前期准备
  • 三. 实现HandlerMethodArgumentResolver接口
  • 四. 将自定义的方法参数解析器放入配置类中
  • 五. Controller层接收数据
  • 六. 效果



一. 需求

现在项目有这样的一个需求,前端在请求头中传入userId,后端需要在controller中从请求头中获取该id,然后根据id查询用户的相关信息,并将信息封装到一个Map中

首先我们可以想到的就是将id传入到service层,然后在service层中进行数据库查询。

在service中进行操作的这种方式不够灵活,因此我们考虑:能否在类方法的参数上,自动的通过请求头中的id进行查询,然后将查询到的结果直接绑定到类方法的参数上呢?

Spring中提供了HandlerMethodArgumentResolver接口,我们可以实现该接口完成我们的需求。


二. 前期准备

⏹定义一个注解,用于标识请求头解析

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface HeaderAnalysis {
    
}

⏹定义前台HTML

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>
    <button id="test">发送带自定义请求头的请求</button>
</div>
<script th:src="@{/js/public/jquery-3.6.0.min.js}"></script>
<script th:inline="javascript">

    $("#test").click(function() {
        
        // ⏹提交到后台的数据
        const param = {
            hobby: "睡觉",
            birthday: "2022/12/21",
        };

        $.ajax({
            url: "/test19/test",
            type: 'POST',
            data: JSON.stringify(param),
            contentType : 'application/json;charset=utf-8',
            // ⏹设置请求头
            beforeSend: function(xhr) {
                xhr.setRequestHeader("userId", "110");
            },
            dataType: 'json',
            success: function (data, status, xhr) {
                console.log(data);
            }
        });
    });
    
</script>
</body>
</html>

三. 实现HandlerMethodArgumentResolver接口

import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
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;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// ⏹自定义方法参数解析器实现HandlerMethodArgumentResolver接口
@Component
public class MyMethodArgumentResolver implements HandlerMethodArgumentResolver {

    // 模拟从数据库查询到的信息
    private static Map<String, List<String>> userInfoMap = new HashMap<String, List<String>>(){
        {
            put("110", Arrays.asList("jmw", "18"));
            put("120", Arrays.asList("fengyehong", "78"));
        }
    };

    // ⏹判断是否支持参数解析
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // 🤔如果参数的类型为Map
        boolean result = parameter.getParameterType().isAssignableFrom(Map.class)
                // 🤔并且该参数还被自定义的HeaderAnalysis注解修饰的话
                && parameter.hasParameterAnnotation(HeaderAnalysis.class);

        // 只有满足了上面两个条件,该参数解析器才会起作用
        return result;
    }

    // ⏹如果支持参数解析的话,就进行参数解析
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        // 🤔将一些默认的用户信息放入request的attribute中
        request.setAttribute("userInfoList", Arrays.asList("fengyehong", "29"));

        // 🤔获取请求头userId
        String userId = request.getHeader("userId");

        // 如果本次请求中不包含userId或者userId为非法id的话,则返回null
        if (ObjectUtils.isEmpty(userId) || !userInfoMap.containsKey(userId)) {
            return null;
        }

        // 模拟根据用户的id查询用户的信息,然后返回
        List<String> userList = userInfoMap.get(userId);
        return new HashMap<String, String>() {
            {
                put("userName", userList.get(0));
                put("userAge", userList.get(1));
            }
        };
    }
}

四. 将自定义的方法参数解析器放入配置类中

  • WebMvcConfigurer为Spring提供的配置接口
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;

@Configuration
public class InternationalConfig implements WebMvcConfigurer {
	
	// 注入自定义的方法参数解析器
    @Resource
    private MyMethodArgumentResolver myMethodArgumentResolver;

    // ⏹将自定义的方法参数解析器放入解析器列表中
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(myMethodArgumentResolver);
    }
}

五. Controller层接收数据

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("/test19")
public class Test19Controller {

    @GetMapping("/init")
    public ModelAndView init() {

        // 指定跳转的页面
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("test19");

        return modelAndView;
    }

    @PostMapping("/test")
    @ResponseBody
    public void test(
            // 解析请求头得到的用户信息
            @HeaderAnalysis Map<String, String> userInfoMap,
            // ajax请求提交json到后台的数据
            @RequestBody Test19Form form,
            // HandlerMethodArgumentResolver接口的实现类中放入requestAttribute中的数据
            @RequestAttribute(value = "userInfoList", required = false) List<String> userInfoList,
            // 获取cookie中的key为JSESSIONID的数据
            @CookieValue(value = "JSESSIONID", required = false) String sessionId
    ) {

        System.out.println("解析请求头得到的用户信息: " + userInfoMap);
        System.out.println("ajax请求提交json到后台的数据: " + form);
        System.out.println("RequestAttribute的数据为: " + userInfoList);
        System.out.println("cookie中JSESSIONID的数据为: " + sessionId);
    }
}

六. 效果

springboot 参数 可以为空_spring boot