在实际开发的时候,当前端参数传输传递并解析的过程中也许会做许多重复的工作,例如:使用token进行登录认证的时候,每一次token传到服务端之后都要在service中手动进行一次转化,变成实际的uid。
要解决这个问题,就需要对spring boot的RequestMapping方法参数赋值的过程进行调试,当方法中的参数为基本类型或者包装类型的时候,调试的请求如下。
@GetMapping("/param")
public Object param(String uid,String name){
return uid;
}
通过调试可以发现,参数赋值发生在RequestParamMethodArgumentResolver.resolveName()方法中,该方法通过RequestMapping中方法的参数名调用request.getParameterValues(name)以获取请求的值,从而进行参数的赋值。
还有另外一种情况,就是当RequestMapping方法参数为pojo对象时,spring处理会不一样。
@GetMapping("/bean")
private Object gets(User user){
return user;
}
public class User {
private String uid;
private String name;
}
通过调试可以发现,在RequestMapping中参数为POJO类型的时候,参数赋值发生在WebUtils.getParametersStartingWith()方法中,这里会通过request.getParameterNames()方法获取所有传递过来的参数并遍历它。在遍历的过程中,会根据遍历的item去匹配pojo的参数并通过request.getParameterValues(paramName)进行赋值。
如上就是spring对RequestMapper的参数赋值的原理了,主要是通过request.getParameterValues(paramName)进行参数值的获取实现的。再次梳理一下需求,当客户端传递了一个token到服务端之后,服务端需要对token进行认证,然后得到uid并映射到RequestMapper对应的方法中参数uid,或者pojo中的uid属性中。
通过源码分析并结合需求,解决思路如下:
使用TokenRequestWrapper包装类对ServletRequest中的getParameterValues和getParameterNames方法进行重写
public class TokenRequestWrapper extends HttpServletRequestWrapper {
TokenRequestWrapper(HttpServletRequest request) {
super(request);
}
/**
* 修改此方法主要是因为当RequestMapper中的参数为pojo类型时,
* 会通过此方法获取所有的请求参数并进行遍历,对pojo属性赋值
* @return
*/
@Override
public Enumeration<String> getParameterNames() {
Enumeration<String> enumeration = super.getParameterNames();
ArrayList<String> list = Collections.list(enumeration);
//当有token字段时动态的添加uid字段
if (list.contains("token")){
list.add("uid");
return Collections.enumeration(list);
}else {
return super.getParameterNames();
}
}
@Override
public String[] getParameterValues(String name) {
if ("uid".equals(name)){
return new String[]{TokenUtil.verifyToken(getParameter("token"))};
}
return super.getParameterValues(name);
}
}
新建一个filter并添加到filter调用链中,通过chain.doFilter把对ServletRequest包装过后的TokenRequestWrapper传递给spring进行处理。
@Component
public class TokenFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
chain.doFilter(new TokenRequestWrapper((HttpServletRequest) req), res);
}
}
测试
接口:/param?token=213&name=231
接口:/bean?token=213&name=231