今天项目遇到一个问题,我们项目用户验证和权限验证的信息(licence)是在http头中设置的,百度了一下,只有ajax才能设置头信息,form表单是无法设置的,但是我突然想起springMVC关于form表单解决put、delete提交方式的问题,我灵机一动,于是模仿springMVC实现了设置自定义header的功能。
起源
项目使用的是SSM框架,废话不多说,我们先看springMVC对于form表单提交put、delete请求问题的解决方案,springMVC是使用了一个过滤器,使之用用户只需在form表单增加一个隐藏域_method即可,比如下面这样:
_method里的值就是你要提交方式,具体情况大家自己百度我就细说了。
springmvc在web.xml中配置是这样的
httpMethodFilter
org.springframework.web.filter.HiddenHttpMethodFilter
httpMethodFilter
SpringMVC
然后我们来看springMVC的源码:
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.filter;
import java.io.IOException;
import java.util.Locale;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* {@link javax.servlet.Filter} that converts posted method parameters into HTTP methods,
* retrievable via {@link HttpServletRequest#getMethod()}. Since browsers currently only
* support GET and POST, a common technique - used by the Prototype library, for instance -
* is to use a normal POST with an additional hidden form field ({@code _method})
* to pass the "real" HTTP method along. This filter reads that parameter and changes
* the {@link HttpServletRequestWrapper#getMethod()} return value accordingly.
*
* The name of the request parameter defaults to {@code _method}, but can be
* adapted via the {@link #setMethodParam(String) methodParam} property.
*
*
NOTE: This filter needs to run after multipart processing in case of a multipart
* POST request, due to its inherent need for checking a POST body parameter.
* So typically, put a Spring {@link org.springframework.web.multipart.support.MultipartFilter}
* before this HiddenHttpMethodFilter in your {@code web.xml} filter chain.
*
* @author Arjen Poutsma
* @since 3.0
*/
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
/** Default method parameter: {@code _method} */
public static final String DEFAULT_METHOD_PARAM = "_method";
private String methodParam = DEFAULT_METHOD_PARAM;
/**
* Set the parameter name to look for HTTP methods.
* @see #DEFAULT_METHOD_PARAM
*/
public void setMethodParam(String methodParam) {
Assert.hasText(methodParam, "'methodParam' must not be empty");
this.methodParam = methodParam;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String paramValue = request.getParameter(this.methodParam);
if ("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
HttpServletRequest wrapper = new HttpMethodRequestWrapper(request, method);
filterChain.doFilter(wrapper, response);
}
else {
filterChain.doFilter(request, response);
}
}
/**
* Simple {@link HttpServletRequest} wrapper that returns the supplied method for
* {@link HttpServletRequest#getMethod()}.
*/
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
super(request);
this.method = method;
}
@Override
public String getMethod() {
return this.method;
}
}
}
重点我们来看他写的HttpMethodRequestWrapper这个内部类,这个类继承HttpServletRequestWrapper,而HttpServletRequestWrapper我进去看了下都是调用更上层的方法自己并没有做什么事情,再往上我就没去看了。我理解的他的原理是:request在得到method时时使用getMethod方法的,所以他重写了getMethod方法,从而可以把_method的值当做method。
那么既然这样,我也可以把_header的值当做header啊,而request获取header的方法是public String getHeader(String name),所以我就写了下面这样的过滤器:
package com.zs.tools;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.filter.HiddenHttpMethodFilter;
/**
* 张顺,2017-2-28
* 处理form表单头的过滤器,
* 如果表单有_header字段,可以自动将该字段转为request的header头信息(增加一条头)
* @author it023
*/
public class MyHiddenHttpMethodFilter extends HiddenHttpMethodFilter{
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String header=request.getParameter("_header");
if (header!=null && !header.trim().equals("")) {
HttpServletRequest wrapper = new HttpHeaderRequestWrapper(request,header);
super.doFilterInternal(wrapper, response, filterChain);
}else {
super.doFilterInternal(request, response, filterChain);
}
}
private static class HttpHeaderRequestWrapper extends HttpServletRequestWrapper{
private final String header;
public HttpHeaderRequestWrapper(HttpServletRequest request,String licence) {
super(request);
this.header=licence;
}
@Override
public String getHeader(String name) {
if (name!=null &&
name.equals("licence") &&
super.getHeader("licence")==null) {
return header;
}else {
return super.getHeader(name);
}
}
}
}
然后,在web.xml中配置一下,我是放在HiddenHttpMethodFilter前面的。
httpHeaderFilter
com.zs.tools.MyHiddenHttpMethodFilter
httpHeaderFilter
SpringMVC