参考资料
- 【SpringBoot】HandlerMethodArgumentResolver的简单使用
- HandlerMethodArgumentResolver(参数解析器)的作用+使用小案例
- 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);
}
}
六. 效果