spring boot 自定義參數過濾器,把傳入的空字符轉換成null方式
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、通過創(chuàng)建FilterRegistrationBean的方式
- 2、 使用@WebFilter
在spring中這些過濾器的創(chuàng)建都需要交給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方法,所以該字節(jié)流只能被獲取一次。所以如果需要對表單提交的json參數處理,則getInputStream()方法需要重寫。網上比較流行的做法是,將字節(jié)數據保存,然后封裝成字節(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()方法是互斥的,只能調用一次。同時調用會拋出異常 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中無法獲取到空數據,很疑惑。 最后發(fā)現導錯包了 【苦笑】
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、創(chuàng)建Java配置類
有多個filter就創(chuàng)建多個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 { /** *該過濾器用于對請求參數做去空格處理 *@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中的執(zhí)行順序 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字符串前后去空格??梢詤⒖家韵麓a處理。
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)); } }
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Spring Boot創(chuàng)建可執(zhí)行jar包的實例教程
這篇文章主要介紹了Spring Boot創(chuàng)建可執(zhí)行jar包的實例教程,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-02-02spring boot hutool整合email的詳細過程
這篇文章主要介紹了spring boot hutool整合email的相關知識,本文介紹兩種方式發(fā)送email文件,結合實例代碼給大家介紹的非常詳細,需要的朋友可以參考下2023-03-03intellij idea設置統(tǒng)一JavaDoc模板的方法詳解
這篇文章主要介紹了intellij idea設置統(tǒng)一JavaDoc模板的方法詳解,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04【spring-boot】快速構建spring-boot微框架的方法
本篇文章主要介紹了【spring-boot】快速構建spring-boot微框架的方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12Springmvc ResponseBody響應json數據實現過程
這篇文章主要介紹了Springmvc ResponseBody響應json數據實現過程,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-10-10