spring boot 自定义参数过滤器,把传入的空字符转换成null方式

网友投稿 233 2022-12-14

spring boot 自定义参数过滤器,把传入的空字符转换成null方式

目录spring boot 滤器,把传入的空字符转换成null自定义参数处理器应用启动类springboot过滤器对请求参数去空格处理使用 FilterRegistrationBean 注册过滤器使用@WebFilter注册过滤器增加jsON字符串的处理

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 argumentResolvers) {

super.addArgumentResolvers(argumentResolvers);

argumentResolvers.add(new MyStringArgumentResolver());

}

}

springboot过滤器对请求参数去空格处理

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

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

String getParameter(String name);//键值对参数

Map getParameterMap();//键值对参数

Enumeration getParameterNames();//键值对参数

String[] getParameterValues(String name);//键值对参数

ServletInputStream getInputStream();// 文本参数,例如类型 application/json 等

BufferedReader getReader(); //文本参数,例如类型 application/json 等

Collection 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 params = new HashMap();//保存处理后的参数

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> entrys=params.entrySet();

for(Entry entry :entrys) {

String[] values=entry.getValue();

for(int i=0;i

values[i] = values[i].trim();

}

this.params.put(entry.getKey(), values);

}

}

@Override

public Enumeration getParameterNames() {//重写getParameterNames()

return new Vector(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 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

*/

@Bean(name="parameterTrimFilter") //不写name属性,默认beanName为方法名

public FilterRegistrationBean parameterTrimFilter() {

FilterRegistrationBean 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 urlPatterns)//覆盖之前已注册的url

void addServletNames(String... servletNames)// 添加过滤的servletName

void setServletNames(Collection 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 = "{\http://"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 keySet = jsonObject.keySet();

for (String key : keySet) {

parseObject(jsonObject, key);

}

}

public static void parseJSONArray(JSONArray jsonArray) {

if (jsonArray == null || jsonArray.isEmpty()) {

return;

}

fowjHtamr (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));

}

}

values[i] = values[i].trim();

}

this.params.put(entry.getKey(), values);

}

}

@Override

public Enumeration getParameterNames() {//重写getParameterNames()

return new Vector(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 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

*/

@Bean(name="parameterTrimFilter") //不写name属性,默认beanName为方法名

public FilterRegistrationBean parameterTrimFilter() {

FilterRegistrationBean 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 urlPatterns)//覆盖之前已注册的url

void addServletNames(String... servletNames)// 添加过滤的servletName

void setServletNames(Collection 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 = "{\http://"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 keySet = jsonObject.keySet();

for (String key : keySet) {

parseObject(jsonObject, key);

}

}

public static void parseJSONArray(JSONArray jsonArray) {

if (jsonArray == null || jsonArray.isEmpty()) {

return;

}

fowjHtamr (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));

}

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:解决Properties属性文件中的值有等号和换行的小问题
下一篇:springboot接收http请求,解决参数中+号变成空格的问题
相关文章

 发表评论

暂时没有评论,来抢沙发吧~