Springboot 在Request parameter 中添加参数
最近有个想法,在 controller 层直接通过 @RequestParam 注解获取用户 id,减少每次查询数据库次数,于是开始尝试
·
背景
最近有个想法,想把通过 JWT 获取的用户 userId 直接手动塞到 request parameter 中,在 controller 层直接通过 @RequestParam
注解获取,减少调用次数,或者直接读 Redis 把整个用户信息塞进去,于是开始尝试
刚开始,我定位到 @RequestParam
注解读取的是 request.getParameterMap()
中的值,所以我进行了以下尝试:
我拦截到 request 并 put 一个值到这个 Map, 然后 Controller 进行调用。
request.getParameterMap().put("name", new String[]{"value1", "value2"});
但是并不行,这个 Map 在返回之前被设置成了不可修改的类型, 所以会报如下错误:
java.lang.IllegalStateException: Cannot find message associated with key parameterMap.
locked at org.apache.catalina.util.ParameterMap.put(ParameterMap.java:213)
所以,我们必须绕过这层限制。
如何做
有两个方法可以:
- 第一个是把这个Map 拿出来,修改不可改变类型并添加一个值,然后想办法塞回去
- 另一种就是创建一个新的 Request 接收代理,然后修改完后返回新的 Request
第一个觉得太麻烦了,并且绕过他的限制可能会出现安全问题,所以采用第二种。
步骤一:定义新的 Request
首先我们先自定义一个 Request
import java.util.Collections;
import java.util.Enumeration;
import java.util.Map;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class EnhancedHttpRequest extends HttpServletRequestWrapper
{
private final Map<String, String[]> modifiableParameters;
private Map<String, String[]> allParameters = null;
/**
* Create a new request wrapper that will merge additional parameters into
* the request object without prematurely reading parameters from the
* original request.
*
* @param request
* @param additionalParams
*/
public EnhancedHttpRequest (final HttpServletRequest request,
final Map<String, String[]> additionalParams)
{
super(request);
modifiableParameters = new TreeMap<>();
modifiableParameters.putAll(additionalParams);
}
@Override
public String getParameter(final String name)
{
String[] strings = getParameterMap().get(name);
if (strings != null)
{
return strings[0];
}
return super.getParameter(name);
}
@Override
public Map<String, String[]> getParameterMap()
{
if (allParameters == null)
{
allParameters = new TreeMap<>();
allParameters.putAll(super.getParameterMap());
allParameters.putAll(modifiableParameters);
}
//Return an unmodifiable collection because we need to uphold the interface contract.
return Collections.unmodifiableMap(allParameters);
}
@Override
public Enumeration<String> getParameterNames()
{
return Collections.enumeration(getParameterMap().keySet());
}
@Override
public String[] getParameterValues(final String name)
{
return getParameterMap().get(name);
}
}
步骤二:新增过滤器
然后,找一个新建一个过滤器,然后加载它
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class AppendParametersFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
Map<String, String[]> additionalParams = new HashMap<>();
additionalParams.put("user_id", new String[] {"215258456658"});
additionalParams.put("account_id", new String[] {"1421823141412"});
EnhancedHttpRequest enhancedHttpRequest = new EnhancedHttpRequest((HttpServletRequest) request, additionalParams);
// pass the request along the filter chain
chain.doFilter(enhancedHttpRequest, response);
}
@Override
public void destroy() { }
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
}
步骤三:加载过滤器,使之生效
由于我用的是 Spring Security 所以加载在了 token 验证后
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// 忽略其他配置
/**
* Configure security settings
*/
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.addFilterBefore(new AppendParametersFilter(), ChannelProcessingFilter.class));
// 忽略其他配置
}
测试
编写 controller 进行测试
@GetMapping(value = "/test")
public ResponseEntity<?> test(@RequestParam(name = "user_id") String userId) {
System.out.println(userId);
return ResponseEntity.ok(userId);
}
可以成功拿到数据
小结
最终成功将数据塞入 request 的 parameter 中,但是由于是 Map 如果前端也传递了同名参数,就会产生覆盖,导致问题。
所以建议还是不要简单使用String 类型,而是封装一个对象进行传输,或者采用 自定义注解的方式进行配置,示例如下:
参考链接:Spring Boot自定义注解获取当前登录用户信息
@RequestMapping(value = "/getUser", method = RequestMethod.GET)
public Map<String, Object> queryUser( @CurrentUser UserInfoVO userInfo) {
System.out.println(userInfo);
}
参考链接
更多推荐
已为社区贡献4条内容
所有评论(0)