springboot使用过滤器,实现对请求参数去空格处理

测试人员提出,对所有接口的请求参数去空格处理。 因为接口比较多,所以考虑使用spring mvc的拦截器来实现,但是使用拦截器能获取到请求参数,却无法将修改后的参数返给HttpServletRequest 对象。

HttpServletRequest 提供的获取参数的方法:

String getParameter(String name);//键值对参数
Map<String,String[]> getParameterMap();//键值对参数
Enumeration <String> getParameterNames();//键值对参数
String[] getParameterValues(String name);//键值对参数
ServletInputStream getInputStream();// 文本参数,例如类型 application/json 等
BufferedReader getReader(); //文本参数,例如类型 application/json 等
Collection<Part> getParts();//表单提交, multipart/form-data	

之后考虑使用过滤器,在网上找到了方案。使用自定义Request对象继承HttpServletRequestWrapper,获取从过滤器传入的HttpServletRequest对象提取参数,并重写HttpServletRequestWrapper获取参数的方法。

springboot添加过滤器有两种方式:

1、通过创建FilterRegistrationBean的方式
2、 使用@WebFilter

在spring中这些过滤器的创建都需要交给spring容器管理

使用 FilterRegistrationBean 注册过滤器

  1. 封装request对象实体
    继承HttpServletRequestWrapper 类用于扩展request。同时如果修改了原来request中参数内容,需要将其修改后的数据返回,所以需要重写获取参数的方法。

    	package com.demo.springBootProject;
    	
    	import java.io.ByteArrayInputStream;
    	import java.io.IOException;
    	import java.io.InputStream;
    	import java.util.Enumeration;
    	import java.util.HashMap;
    	import java.util.Map;
    	import java.util.Map.Entry;
    	import java.util.Set;
    	import java.util.Vector;
    	import javax.servlet.ReadListener;
    	import javax.servlet.ServletInputStream;
    	import javax.servlet.http.HttpServletRequest;
    	import javax.servlet.http.HttpServletRequestWrapper;
    	import org.apache.http.entity.ContentType;
    	import org.apache.poi.util.IOUtils;
    	
    	/**
    	 *重新封装request,并对请求参数做去空格处理
    	 */
    	public class ParameterTrimRequest  extends HttpServletRequestWrapper{
    	
    		 private Map<String, String[]> params = new HashMap<String, String[]>();//保存处理后的参数			
    		 private byte[] content; 
    		 
    		    public ParameterTrimRequest(HttpServletRequest request) {
    		        super(request);
    		        this.params.putAll(request.getParameterMap());
    		        this.modifyParameterValues(); //自定义方法,用于参数去重
    		        if(ContentType.APPLICATION_JSON.getMimeType().equals(request.getContentType())){//对application/json数据格式的数据进行去空格处理
    		        	this.content=IOUtils.toByteArray(request.getInputStream());//获取文本数据;
    					// 对json字符串进行处理	
    					//....................
    		        	}
    		    }
    		 
    			public void modifyParameterValues() {//将parameter的值去除空格后重写回去
    				Set<Entry<String,String[]>> entrys=params.entrySet();
    				for(Entry<String,String[]> entry :entrys) {
    					String[] values=entry.getValue();
    					for(int i=0;i<values.length;i++) {
    						values[i] = values[i].trim();
    					}
    					this.params.put(entry.getKey(), values);
    				}
    		    }
    		 
    		    @Override
    		    public Enumeration<String> getParameterNames() {//重写getParameterNames()
    		        return new Vector<String>(params.keySet()).elements();
    		    }
    		    
    		    
    		    @Override
    		    public String getParameter(String name) {//重写getParameter()
    		        String[] values = params.get(name);
    		        if (values == null || values.length == 0) {
    		            return null;
    		        }
    		        return values[0];
    		    }
    		    
    		    @Override
    		    public String[] getParameterValues(String name) {//重写getParameterValues()
    		        return params.get(name);
    		    }
    		    
    		   @Override
    		   public Map<String,String[]> getParameterMap(){ //重写getParameterMap()
    			   return this.params;
    		   }
    
    		@Override  
      		public ServletInputStream getInputStream() throws IOException {  
          	//  这种获取的参数的方式针对于内容类型为文本类型,比如Content-Type:text/plain,application/json,text/html等
          	//在springmvc中可以使用@RequestBody 来获取 json数据类型
      	 	//其他文本类型不做处理,重点处理json数据格式
    			if(!super.getHeader("Content-Type").equalsIgnoreCase("application/json")){  
                	   return super.getInputStream();  
            	 }else{
            	 //根据自己的需要重新指定方法
            	 	ByteArrayInputStream in =new ByteArrayInputStream(this.content);
    				return new ServletInputStream() {
    					@Override
    					public int read() throws IOException {
    						return in.read();
    					}
    					
    					@Override
    					public int read(byte[] b, int off, int len) throws IOException {
    						return in.read(b, off, len);
    					}
    	
    					@Override
    					public int read(byte[] b) throws IOException {
    						return in.read(b);
    					}
    					
    					@Override
    					public void setReadListener(ReadListener listener) {
    					}
    					
    					@Override
    					public boolean isReady() {
    						return false;
    					}
    					
    					@Override
    					public boolean isFinished() {
    						return false;
    					}
    	
    					@Override
    					public long skip(long n) throws IOException {
    						return in.skip(n);
    					}
    	
    					@Override
    					public void close() throws IOException {
    						in.close();
    					}
    	
    					@Override
    					public synchronized void mark(int readlimit) {
    						in.mark(readlimit);
    					}
    	
    					@Override
    					public synchronized void reset() throws IOException {
    						in.reset();
    					}
    					};
            	 }  
         	} 			
    	}
    

    返回结果是一个ServletInputStream它是InputStream的子类,因为没有重写reset方法,所以该字节流只能被获取一次。所以如果需要对表单提交的json参数处理,则getInputStream()方法需要重写。网上比较流行的做法是,将字节数据保存,然后封装成字节流。

    ServletRequest 源码中获取字节流和字符流的方法

    /**
    *  Either this method or {@link #getInputStream} may be called to read the body, not both.
    *  @exception IllegalStateException
    *       if {@link #getInputStream} method has been called on this request
    **/
    public ServletInputStream getInputStream() throws IOException;
    public BufferedReader getReader() throws IOException;
    
    

    getInputSteam和getReader()方法是互斥的,只能调用一次。同时调用会抛出异常 IllegalStateException。
    getInputSteam 方法重写最好限定数据类型。比如说文本类型(application/json,text/plain)。
    对于Multipart/form-data提交类型,Tomcat会先调用了getInputStream方法获取请求体中的数据。

    //Request.class      parseParameters
     if ("multipart/form-data".equals(contentType)) {
                    parseParts(false);
                    success = true;
                    return;
       }
    

    这样在后边再使用getInputSteam方法流中的数据就已经被读取完了。如果再不小心调用了getReader 方法 ,就会抛出异常。

    java.lang.IllegalStateException: getInputStream() has already been called for this request
    

    :::本地测试的结果,在filter中和spring mvc的拦截器中可以反复获取流数据。但是在controller中无法获取到空数据,很疑惑。 最后发现导错包了 【苦笑】

  2. 自定义过滤器

    package com.demo.springBootProject.filter;
    
    import java.io.IOException;
    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 ParameterTrimFilter implements Filter{
    
    	@Override
    	public void init(FilterConfig filterConfig) throws ServletException {
    	}
    
    	@Override
    	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    			throws IOException, ServletException {
    		ParameterTrimRequest trimReqeust= new ParameterTrimRequest((HttpServletRequest)request);
    		chain.doFilter(trimReqeust, response);
    	}
    
    	@Override
    	public void destroy() {
    	}
    }
    
  3. 创建Java配置类
     有多个filter就创建多个FilterRegistrationBean ,若需注明filter的执行顺序,可通过registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE )配置,值越大,执行顺序越靠后

    package com.demo.springBootProject.config;
    
    import java.util.ArrayList;
    import java.util.List;
    import javax.servlet.DispatcherType;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import com.demo.springBootProject.filter.ParameterTrimFilter;
    
    /**
     * 用于添加自定义的bean
     */
    @Configuration
    public class CustomBean {
    
    	/**
    	 *该过滤器用于对请求参数做去空格处理
    	 *@return FilterRegistrationBean<ParameterTrimFilter>
    	 */
    	@Bean(name="parameterTrimFilter") //不写name属性,默认beanName为方法名
    	public FilterRegistrationBean<ParameterTrimFilter> parameterTrimFilter() {
    		FilterRegistrationBean<ParameterTrimFilter> filter=new FilterRegistrationBean<>();
    		filter.setDispatcherTypes(DispatcherType.REQUEST);  
    		filter.setFilter(new ParameterTrimFilter()); //必须设置
    		filter.addUrlPatterns("/*"); //拦截所有请求,如果没有设置则默认“/*”
    		filter.setName("parameterTrimFilter"); //设置注册的名称,如果没有指定会使用Bean的名称。此name也是过滤器的名称
    		filter.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);//该filter在filterChain中的执行顺序
    		return filter;
    	}
    }
    

注意: 以上配置类所在包或者过滤器所在包必须在spring boot注解可以扫描的包或者子包下。如果springboot没有设置扫描包,则springboot会扫描启动类所在的包及其子包。

FilterRegistrationBean提供了多种方式来注册拦截的请求。

 void addUrlPatterns(String... urlPatterns)//向filter追加注册url
 void setUrlPatterns(Collection<String> urlPatterns)//覆盖之前已注册的url
 void addServletNames(String... servletNames)// 添加过滤的servletName
 void setServletNames(Collection<String> servletNames)//覆盖之前的拦截的servletName

拦截url和拦截servlet可以一起使用

使用@WebFilter注册过滤器

package com.demo.springBootProject.controller;

import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebFilter(filterName="test",urlPatterns= {"/*"})
public class TestFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
		//doSomthing
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
	}
	
}

参考文献:spring boot 过滤器去除请求参数前后空格

增加JSON字符串的处理

以上处理的是简单的数据类型,如果是json字符串,应当先解析json字符串,将其中的value字符串前后去空格。可以参考以下代码处理。

     public static void main(String[] args) {
               String str = "[\" 1\" ,\"2  \",\"4  \"]";
//        str = "{\"name\":\"张三  \"}";
//        str = "{\"name\":  {\"first_name\": \"张  \",\"last_name\":\"  三  \"}}";
        str = "[{'name':'  张三  '},{'name':'  李四  '},{'name':19}]";
       
        Object jsonObj = JSON.parse(str);
        if (jsonObj instanceof JSONObject) {
            JSONObject jsonObject = (JSONObject) jsonObj;
            parseJsonObject(jsonObject);
        } else if (jsonObj instanceof JSONArray) {
            JSONArray jsonArray = (JSONArray) jsonObj;
            parseJSONArray(jsonArray);
        }
        System.out.println(jsonObj);
    }

    public static void parseJsonObject(JSONObject jsonObject) {
        Set<String> keySet = jsonObject.keySet();
        for (String key : keySet) {
            parseObject(jsonObject, key);
        }
    }

    public static void parseJSONArray(JSONArray jsonArray) {
        if (jsonArray == null || jsonArray.isEmpty()) {
            return;
        }
        for (int i = 0; i < jsonArray.size(); i++) {
            parseArray(jsonArray, i);
        }

    }

    public static void parseObject(JSONObject jsonObject, String key) {
        Object keyObj = jsonObject.get(key);
        if (keyObj == null) {
            return;
        }
        if (keyObj instanceof String) {
            jsonObject.put(key, ((String) keyObj).trim());
        } else if (keyObj instanceof JSONObject) {
            //解析json 对象
            parseJsonObject(((JSONObject) keyObj));
        } else if (keyObj instanceof JSONArray) {
            //解析json 数组
            parseJSONArray(((JSONArray) keyObj));
        }
    }

    public static void parseArray(JSONArray jsonArray, int index) {
        Object keyObj = jsonArray.get(index);
        if (keyObj == null) {
            return;
        }
        if (keyObj instanceof String) {
            jsonArray.set(index, ((String) keyObj).trim());
        } else if (keyObj instanceof JSONObject) {
            //解析json 对象
            parseJsonObject(((JSONObject) keyObj));
        } else if (keyObj instanceof JSONArray) {
            //解析json 数组
            parseJSONArray(((JSONArray) keyObj));
        }
    }

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
可以通过自定义一个过滤器实现对所有请求参数清除前后空格的操作。以下是实现方式: 1. 创建一个类,继承 `OncePerRequestFilter` 类,重写 `doFilterInternal` 方法,该方法用于拦截请求处理请求参数。 ```java import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class TrimRequestParamsFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 获取所有请求参数名 Enumeration<String> paramNames = request.getParameterNames(); // 遍历请求参数并清除前后空格 while (paramNames.hasMoreElements()) { String paramName = paramNames.nextElement(); String[] paramValues = request.getParameterValues(paramName); for (int i = 0; i < paramValues.length; i++) { paramValues[i] = paramValues[i].trim(); } request.setAttribute(paramName, paramValues); } filterChain.doFilter(request, response); } } ``` 2. 在Spring Boot项目中注册该过滤器。 ```java import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FilterConfig { @Bean public FilterRegistrationBean<TrimRequestParamsFilter> trimRequestParamsFilter() { FilterRegistrationBean<TrimRequestParamsFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new TrimRequestParamsFilter()); // 设置过滤器的执行顺序,数字越小越先执行 registrationBean.setOrder(1); // 设置需要过滤的请求路径 registrationBean.addUrlPatterns("/*"); return registrationBean; } } ``` 该配置文件中创建了一个名为 `trimRequestParamsFilter` 的过滤器,并设置需要过滤的请求路径为 "/*",表示对所有请求进行参数清除操作。最后通过 `setOrder` 方法设置过滤器的执行顺序,数字越小越先执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值