如何修改request參數值並應用到springMvc中
就簡潔地說一下吧。大多數crud都是在controller的handler中進行的,一般的思路就是從request里取到當前user,或者userid,然後再通過這個user/userid去資料庫里進行下一步操作。仔細想想的話,是不是做了一些重複的操作?能不能有一種方法可以從handler的參數中直接取到當前user/userid?這樣不僅可以直接根據其進行下一步操作,而且代碼也減少了冗餘。比如這樣:
@RequestMapping("test")
@ResponseBody
public String test(HttpServletRequest request, String userid) {
userService.doService(userid);
.....
}
我們更希望像這樣直接從參數中拿到當前會話下的user/userid,然後就直接根據其進行其他操作。那麼有沒有辦法實現呢?答案是 有的,請繼續看下面的思路
有關request的參數其實都保存在了一個parameterMap中,一般通過request.getParameterMap()得到,只要我們能在springMvc之前得到user/userid,然後將其添加到request的parameterMap中,那麼在上面的test方法中String userid自然可以直接取到對應的值。但是由於tomcat的開發人員不想我們直接修改request的參數值(做了手腳),所以類似request.getParameterMap().put(xxx,xxx)是行不通的,所以需要另闢蹊徑。https://blog.csdn.net/xieyuooo/article/details/8447301這篇文章講的挺清楚的,這裡就使用其中的一種方法。
繼承HttpServletRequestWrapper包裝一個自己的request
大致代碼都是差不多的,如下
public class HttpRequest extends HttpServletRequestWrapper implements MyHttpRequest {
private Map<String, String[]> params = new HashMap<String, String[]>();
@SuppressWarnings("unchecked")
public HttpRequest(HttpServletRequest request) {
super(request);
params.putAll(request.getParameterMap());
}
@Override
public String getParameter(String name) {
String[] values = params.get(name);
return values == null || values.length == 0 ? null : values[0];
}
@Override
public String[] getParameterValues(String key) {
return params.get(key);
}
@Override
public void setParameter(String key, String value) {
if (value == null)
return;
params.put(key, new String[] { value });
}
@Override
public void setParameter(String key, String[] value) {
if (value == null)
return;
params.put(key, value);
}
@Override
public void setParameter(String key, Object value) {
if (value == null)
return;
params.put(key, new String[] { String.valueOf(value) });
}
@Override
public Map<String, String[]> getParameterMap() {
return params;
}
@Override
public Enumeration<String> getParameterNames() {
Enumeration<String> enumeration = new MyEnumeration<String>(params.keySet().iterator());
return enumeration;
}
}
對應的MyEnumeration重寫下Enumeration的方法(My前綴可能有些人看的不舒服,認為有獨佔別人東西的感覺,但是並沒有這個意思,當初加一個My前綴也沒想那麼多,僅僅是為了方便區分而已⊙﹏⊙)
public class MyEnumeration<E> implements Enumeration<E> {
private Iterator<E> iterator;
public MyEnumeration(Iterator<E> iterator) {
this.iterator = iterator;
}
@Override
public boolean hasMoreElements() {
return iterator.hasNext();
}
@Override
public E nextElement() {
return iterator.next();
}
}
這樣request就可以設置參數了,然後在filter中獲取到當前user/userid,並將其作為參數設置到自定義的request中
//setCondition是自定義filter中的一個流程,可以不用太在意
@Override
protected boolean setCondition(MyHttpRequest req, HttpServletResponse response)
throws IOException, ServletException {
Object id = null;
//獲取到當前用戶的id,本質還是從session中取
if ((id = securityService.checkIdentity_s(req)) == null)
return true;
//paramKey可以自己定義,這裡我定義的就是"userid"
req.setParameter(paramKey, id.toString());
System.err.println("now the request is "+req);
return false;
}
最後只需要在chain.doFilter中傳入自定義的request即可
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
//自定義request
MyHttpRequest req = null;
if (request instanceof HttpServletRequest) {
req = new HttpRequest((HttpServletRequest) request);
for (int i = 0; i < filters.size(); i++) {
//HttpFilter是自定義的一個抽象類
HttpFilter filter = filters.get(i);
//調用所有的filter,前面的setCondition函數就是裡面的一個小流程
filter.doFilter(req, response, chain);
}
//一定要注意傳進去的是自定義的request
chain.doFilter(req, response);
} else {
for (int i = 0; i < otherFilters.size(); i++) {
OtherFilter filter = otherFilters.get(i);
filter.doOtherRequest(request, response, chain);
}
chain.doFilter(request, response);
}
}
登錄後(用戶登錄後其資料庫里的userid會被保存到session中)再進行測試,直接在瀏覽器輸入路徑
後台列印,兩種方式都可以取出
這樣,就實現了直接從參數中拿到當前的user/userid,然後就可以直接根據其進行其他操作了,代碼也不再那麼冗餘
說一些額外的(上面沒問題了下面就可以忽略)
1> 為什麼要使用filter,使用攔截器不更方便嗎?
其實最開始我也是使用的Interceptor,但是後面發現不行,看了下dispatcher的源碼,找到了原因所在
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
......
try {
ModelAndView mv = null;
......
// interceptor前置攔截
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// 真正執行handler的方法,返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
......
}
可以看到,真正傳進去的參數都是processedRequest,所以說,即使你在interceptor的前置攔截中將processedRequest的引用指向了自定義的request,在真正處理handler的時候,傳進去的仍然是processedRequest,而不是你自己的request。所以只能在調用doDispatch之前將HttpServletRequest的引用指向自定義的request,而filter恰好可以做到
2> 有關filter的配置,當然是使用spring的代理filter:DelegatingFilterProxy,web.xml如下
<filter>
<filter-name>DelegatingFilterProxy</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>filterManager</param-value>
</init-param>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>DelegatingFilterProxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
然後在spring的配置中聲明filter,如下
<bean id="convinientFilter" name="convinientFilter"
class="com.mine.support.web.filter.ConvinientFilter">
<property name="excludeMappingPath" value="/,/*.log,/*log,*.css,*.js,*.png,*.jpg,*.jpeg"></property>
</bean>
<bean id="filterManager" class="com.mine.support.web.filter.FilterManager">
<property name="filters">
<list>
<ref bean="convinientFilter"/>
</list>
</property>
</bean>
---------------------------------------------------------------------------------------------------------------------------------------------------------------
然後說一下自己的一些事,由於十月打了一整月比賽,所以十月沒時間寫博客,然後我的話基本上有問題我都會回復的,只是有些奇怪我記得11月1號我回復了一個問題了的(確信的眼神),但是今天發現我的回復莫名奇妙不見了,所以也不知道真實情況是什麼,不過既然已經過了這麼久,相信問題也已經自己解決了吧,就不再重複回復了( ̄▽ ̄)
※內核態(Kernel Mode)與用戶態(User Mode)
※springmvc里使用註解進行攔截器配置
TAG:程序員小新人學習 |