spring boot 自定义参数过滤器,把传入的空字符转换成null方式
作者:Orgin5
spring boot 滤器,把传入的空字符转换成null
废话不多说直接上代码
自定义参数处理器
public class MyStringArgumentResolver extends AbstractNamedValueMethodArgumentResolver { @Override protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { return new NamedValueInfo("", false, ValueConstants.DEFAULT_NONE); } @Override protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception { String[] param = request.getParameterValues(name); if(param==null){ return null; } if(StringUtils.isEmpty(param[0])){ return null; } return param[0]; } @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType().equals(String.class); } }
应用启动类
public class Applicaction extends WebMvcConfigurerAdapter { public static void main(String[] ags) { SpringApplication.run(Applicaction.class, ags); } @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { super.addArgumentResolvers(argumentResolvers); argumentResolvers.add(new MyStringArgumentResolver()); } }
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() { } }
增加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)); } }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。