spring boot 自定義參數(shù)過濾器,把傳入的空字符轉(zhuǎn)換成null方式
spring boot 濾器,把傳入的空字符轉(zhuǎn)換成null
廢話不多說直接上代碼
自定義參數(shù)處理器
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); } }
應(yīng)用啟動(dòng)類
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過濾器對(duì)請(qǐng)求參數(shù)去空格處理
測(cè)試人員提出,對(duì)所有接口的請(qǐng)求參數(shù)去空格處理。 因?yàn)榻涌诒容^多,所以考慮使用spring mvc的攔截器來實(shí)現(xiàn),但是使用攔截器能獲取到請(qǐng)求參數(shù),卻無法將修改后的參數(shù)返給HttpServletRequest 對(duì)象。
HttpServletRequest 提供的獲取參數(shù)的方法:
String getParameter(String name);//鍵值對(duì)參數(shù) Map<String,String[]> getParameterMap();//鍵值對(duì)參數(shù) Enumeration <String> getParameterNames();//鍵值對(duì)參數(shù) String[] getParameterValues(String name);//鍵值對(duì)參數(shù) ServletInputStream getInputStream();// 文本參數(shù),例如類型 application/json 等 BufferedReader getReader(); //文本參數(shù),例如類型 application/json 等 Collection<Part> getParts();//表單提交, multipart/form-data
之后考慮使用過濾器,在網(wǎng)上找到了方案。使用自定義Request對(duì)象繼承HttpServletRequestWrapper,獲取從過濾器傳入的HttpServletRequest對(duì)象提取參數(shù),并重寫HttpServletRequestWrapper獲取參數(shù)的方法。
springboot添加過濾器有兩種方式:
- 1、通過創(chuàng)建FilterRegistrationBean的方式
- 2、 使用@WebFilter
在spring中這些過濾器的創(chuàng)建都需要交給spring容器管理
使用 FilterRegistrationBean 注冊(cè)過濾器
1、封裝request對(duì)象實(shí)體
繼承HttpServletRequestWrapper 類用于擴(kuò)展request。同時(shí)如果修改了原來request中參數(shù)內(nèi)容,需要將其修改后的數(shù)據(jù)返回,所以需要重寫獲取參數(shù)的方法。
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,并對(duì)請(qǐng)求參數(shù)做去空格處理 */ public class ParameterTrimRequest extends HttpServletRequestWrapper{ private Map<String, String[]> params = new HashMap<String, String[]>();//保存處理后的參數(shù) private byte[] content; public ParameterTrimRequest(HttpServletRequest request) { super(request); this.params.putAll(request.getParameterMap()); this.modifyParameterValues(); //自定義方法,用于參數(shù)去重 if(ContentType.APPLICATION_JSON.getMimeType().equals(request.getContentType())){//對(duì)application/json數(shù)據(jù)格式的數(shù)據(jù)進(jìn)行去空格處理 this.content=IOUtils.toByteArray(request.getInputStream());//獲取文本數(shù)據(jù); // 對(duì)json字符串進(jìn)行處理 //.................... } } 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 { // 這種獲取的參數(shù)的方式針對(duì)于內(nèi)容類型為文本類型,比如Content-Type:text/plain,application/json,text/html等 //在springmvc中可以使用@RequestBody 來獲取 json數(shù)據(jù)類型 //其他文本類型不做處理,重點(diǎn)處理json數(shù)據(jù)格式 if(!super.getHeader("Content-Type").equalsIgnoreCase("application/json")){ return super.getInputStream(); }else{ //根據(jù)自己的需要重新指定方法 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(); } }; } } }
返回結(jié)果是一個(gè)ServletInputStream它是InputStream的子類,因?yàn)闆]有重寫reset方法,所以該字節(jié)流只能被獲取一次。所以如果需要對(duì)表單提交的json參數(shù)處理,則getInputStream()方法需要重寫。網(wǎng)上比較流行的做法是,將字節(jié)數(shù)據(jù)保存,然后封裝成字節(jié)流。
ServletRequest 源碼中獲取字節(jié)流和字符流的方法
/** * 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()方法是互斥的,只能調(diào)用一次。同時(shí)調(diào)用會(huì)拋出異常 IllegalStateException。
getInputSteam 方法重寫最好限定數(shù)據(jù)類型。比如說文本類型(application/json,text/plain)。
對(duì)于Multipart/form-data提交類型,Tomcat會(huì)先調(diào)用了getInputStream方法獲取請(qǐng)求體中的數(shù)據(jù)。
//Request.class parseParameters if ("multipart/form-data".equals(contentType)) { parseParts(false); success = true; return; }
這樣在后邊再使用getInputSteam方法流中的數(shù)據(jù)就已經(jīng)被讀取完了。如果再不小心調(diào)用了getReader 方法 ,就會(huì)拋出異常。
java.lang.IllegalStateException: getInputStream() has already been called for this request
:::本地測(cè)試的結(jié)果,在filter中和spring mvc的攔截器中可以反復(fù)獲取流數(shù)據(jù)。但是在controller中無法獲取到空數(shù)據(jù),很疑惑。 最后發(fā)現(xiàn)導(dǎo)錯(cuò)包了 【苦笑】
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; /** * 自定義過濾器,用于對(duì)請(qǐng)求參數(shù)去空格處理。 */ 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、創(chuàng)建Java配置類
有多個(gè)filter就創(chuàng)建多個(gè)FilterRegistrationBean ,若需注明filter的執(zhí)行順序,可通過registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE )配置,值越大,執(zhí)行順序越靠后
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 { /** *該過濾器用于對(duì)請(qǐng)求參數(shù)做去空格處理 *@return FilterRegistrationBean<ParameterTrimFilter> */ @Bean(name="parameterTrimFilter") //不寫name屬性,默認(rèn)beanName為方法名 public FilterRegistrationBean<ParameterTrimFilter> parameterTrimFilter() { FilterRegistrationBean<ParameterTrimFilter> filter=new FilterRegistrationBean<>(); filter.setDispatcherTypes(DispatcherType.REQUEST); filter.setFilter(new ParameterTrimFilter()); //必須設(shè)置 filter.addUrlPatterns("/*"); //攔截所有請(qǐng)求,如果沒有設(shè)置則默認(rèn)“/*” filter.setName("parameterTrimFilter"); //設(shè)置注冊(cè)的名稱,如果沒有指定會(huì)使用Bean的名稱。此name也是過濾器的名稱 filter.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);//該filter在filterChain中的執(zhí)行順序 return filter; } }
注意: 以上配置類所在包或者過濾器所在包必須在spring boot注解可以掃描的包或者子包下。如果springboot沒有設(shè)置掃描包,則springboot會(huì)掃描啟動(dòng)類所在的包及其子包。
FilterRegistrationBean提供了多種方式來注冊(cè)攔截的請(qǐng)求。
void addUrlPatterns(String... urlPatterns)//向filter追加注冊(cè)u(píng)rl void setUrlPatterns(Collection<String> urlPatterns)//覆蓋之前已注冊(cè)的url void addServletNames(String... servletNames)// 添加過濾的servletName void setServletNames(Collection<String> servletNames)//覆蓋之前的攔截的servletName
攔截url和攔截servlet可以一起使用
使用@WebFilter注冊(cè)過濾器
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字符串的處理
以上處理的是簡(jiǎn)單的數(shù)據(jù)類型,如果是json字符串,應(yīng)當(dāng)先解析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 對(duì)象 parseJsonObject(((JSONObject) keyObj)); } else if (keyObj instanceof JSONArray) { //解析json 數(shù)組 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 對(duì)象 parseJsonObject(((JSONObject) keyObj)); } else if (keyObj instanceof JSONArray) { //解析json 數(shù)組 parseJSONArray(((JSONArray) keyObj)); } }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Boot創(chuàng)建可執(zhí)行jar包的實(shí)例教程
這篇文章主要介紹了Spring Boot創(chuàng)建可執(zhí)行jar包的實(shí)例教程,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-02-02spring boot hutool整合email的詳細(xì)過程
這篇文章主要介紹了spring boot hutool整合email的相關(guān)知識(shí),本文介紹兩種方式發(fā)送email文件,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03intellij idea設(shè)置統(tǒng)一JavaDoc模板的方法詳解
這篇文章主要介紹了intellij idea設(shè)置統(tǒng)一JavaDoc模板的方法詳解,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04MyBatis實(shí)現(xiàn)萬能Map和模糊查詢
本文主要介紹了MyBatis實(shí)現(xiàn)萬能Map和模糊查詢,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07Java實(shí)現(xiàn)企業(yè)微信回調(diào)配置的詳細(xì)步驟與測(cè)試
這篇文章主要給大家介紹了關(guān)于Java實(shí)現(xiàn)企業(yè)微信回調(diào)配置的詳細(xì)步驟與測(cè)試,企業(yè)微信回調(diào)是指企業(yè)微信通過HTTP?POST請(qǐng)求將業(yè)務(wù)數(shù)據(jù)回調(diào)到指定的URL上,文中給出了詳細(xì)的代碼示例,需要的朋友可以參考下2023-09-09【spring-boot】快速構(gòu)建spring-boot微框架的方法
本篇文章主要介紹了【spring-boot】快速構(gòu)建spring-boot微框架的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-12-12Java中JDBC連接池的基本原理及實(shí)現(xiàn)方式
本文詳細(xì)講解了Java中JDBC連接池的基本原理及實(shí)現(xiàn)方式,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-12-12Springmvc ResponseBody響應(yīng)json數(shù)據(jù)實(shí)現(xiàn)過程
這篇文章主要介紹了Springmvc ResponseBody響應(yīng)json數(shù)據(jù)實(shí)現(xiàn)過程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10