背景
: 使用者在前端頁面中不小心輸入的前後空格,為了防止因為前後空格原因引起業務異常,所以我們需要去除引數的前後空格!
如果我們手動去除引數前後空格,我們可以這樣做
@GetMapping(value = "/manualTrim")
public void helloGet(String userName) {
//手動去空格
userName = userName == null ? null : userName.trim();
//或者通過谷歌工具類手動去空格
String trim = StringUtils.trim(userName);
}
這種方式需要每個介面引數都進行手動的去除首尾空格,顯然會讓程式碼冗餘,並不友好!所以我們應該從專案整體思考,這裡通過過濾器的方式去除請求引數前後空格。
我們來看下大致實現的流程
在SpringBoot中有兩種方式實現自定義Filter:
第一種是使用 @WebFilter
和 @ServletComponentScan
組合註解。
第二種是通過設定類注入 FilterRegistrationBean
物件。
通過FilterRegistrationBean物件可以通過Order屬性改變順序,使用@WebFilter註解的方式只能根據過濾器名的類名順序執行,新增@Order註解是無效的。
既然是通過過濾器獲取請求引數去除引數的首尾空格,那我們應該考慮幾種情況的請求引數
第一種和第二種其實可以通過一種方式實現就是request.getParameter()方法。
Post中body請求引數我們可以通過使用流的方式,呼叫request.getInputStream()獲取流,然後從流中讀取引數。
/**
* 通過FilterRegistrationBean註冊自定義過濾器TrimFilter
*/
@Configuration
public class FilterConfig {
/**
* 註冊去除引數頭尾空格過濾器
*/
@Bean
public FilterRegistrationBean trimFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
//註冊自定義過濾器
registration.setFilter(new TrimFilter());
//過濾所有路徑
registration.addUrlPatterns("/*");
//過濾器名稱
registration.setName("trimFilter");
//優先順序越低越優先,這裡說明最低優先順序
registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
return registration;
}
}
/**
* 自定義過濾器 通過繼承OncePerRequestFilter實現每次請求該過濾器只被執行一次
*/
public class TrimFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain)
throws ServletException, IOException {
//自定義TrimRequestWrapper,在這裡實現引數去空
TrimRequestWrapper requestWrapper = new TrimRequestWrapper(httpServletRequest);
filterChain.doFilter(requestWrapper, httpServletResponse);
}
}
TrimRequestWrapper類,其實也是最重要的一個類,繼承HttpServletRequestWrapper重寫getParameter
,getParameterValues
方法,getInputStream
方法,前面的重寫
可以解決非json的引數首尾去空格,但如果是json請求的引數那就必須重寫getInputStream方法,從流中獲取引數進行處理。
注意
: request的輸入流只能讀取一次
/**
* 自定義TrimRequestWrapper類
*/
@Slf4j
public class TrimRequestWrapper extends HttpServletRequestWrapper {
/**
* 儲存處理後的引數
*/
private Map<String, String[]> params = new HashMap<String, String[]>();
public TrimRequestWrapper(HttpServletRequest request) {
//將request交給父類別,以便於呼叫對應方法的時候,將其輸出
super(request);
//對於非json請求的引數進行處理
if (super.getHeader(HttpHeaders.CONTENT_TYPE) == null ||
(!super.getHeader(HttpHeaders.CONTENT_TYPE).equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE) &&
!super.getHeader(HttpHeaders.CONTENT_TYPE).equalsIgnoreCase(MediaType.APPLICATION_JSON_UTF8_VALUE))) {
setParams(request);
}
}
private void setParams(HttpServletRequest request) {
//將請求的的引數轉換為map集合
Map<String, String[]> requestMap = request.getParameterMap();
log.info("kv轉化前引數:" + JSON.toJSONString(requestMap));
this.params.putAll(requestMap);
//去空操作
this.modifyParameterValues();
log.info("kv轉化後引數:" + JSON.toJSONString(params));
}
/**
* 將parameter的值去除空格後重寫回去
*/
public void modifyParameterValues() {
Set<String> set = params.keySet();
Iterator<String> it = set.iterator();
while (it.hasNext()) {
String key = it.next();
String[] values = params.get(key);
values[0] = values[0].trim();
params.put(key, values);
}
}
/**
* 重寫getParameter 引數從當前類中的map獲取
*/
@Override
public String getParameter(String name) {
String[] values = params.get(name);
if (values == null || values.length == 0) {
return null;
}
return values[0];
}
/**
* 重寫getParameterValues
*/
@Override
public String[] getParameterValues(String name) {
return params.get(name);
}
/**
* 重寫getInputStream方法 post型別的請求引數必須通過流才能獲取到值
* 這種獲取的引數的方式針對於內容型別為文字型別,比如Content-Type:text/plain,application/json,text/html等
* 在springmvc中可以使用@RequestBody 來獲取 json資料型別
* 其他文字型別不做處理,重點處理json資料格式
* getInputStream() ,只有當方法為post請求,且引數為json格式是,會被預設呼叫
*/
@Override
public ServletInputStream getInputStream() throws IOException {
//
if (!super.getHeader(HttpHeaders.CONTENT_TYPE).equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE) &&
!super.getHeader(HttpHeaders.CONTENT_TYPE).equalsIgnoreCase(MediaType.APPLICATION_JSON_UTF8_VALUE)) {
//如果引數不是json格式則直接返回
return super.getInputStream();
}
//為空,直接返回
String json = IOUtils.toString(super.getInputStream(), "utf-8");
if (StringUtils.isEmpty(json)) {
return super.getInputStream();
}
log.info("json轉化前引數:" + json);
//json字串首尾去空格
JSONObject jsonObject = StringJsonUtils.JsonStrTrim(json);
log.info("json轉化後引數:" + jsonObject.toJSONString());
ByteArrayInputStream bis = new ByteArrayInputStream(jsonObject.toJSONString().getBytes("utf-8"));
return new MyServletInputStream(bis);
}
}
因為上面說了三種情況,所以這裡提供了3個介面來進行測試
/**
* 測試介面
*
* @author xub
* @date 2022/10/24 下午5:06
*/
@Slf4j
@RestController
public class ParamController {
/**
* 1、Get請求測試首尾去空格
*/
@GetMapping(value = "/getTrim")
public String getTrim(@RequestParam String username, @RequestParam String phone) {
return username + "&" + phone;
}
/**
* 2、Post方法測試首尾去空格
*/
@PostMapping(value = "/postTrim")
public String postTrim(@RequestParam String username, @RequestParam String phone) {
return username + "&" + phone;
}
/**
* 3、post方法 json入參 測試首尾去空格
*/
@PostMapping(value = "/postJsonTrim")
public String helloUser(@RequestBody UserDO userDO) {
return JSONObject.toJSONString(userDO);
}
}
請求url
http://localhost:8080/getTrim?username=張三 &phone= 18812345678
後臺輸出紀錄檔
: kv轉化前引數:{"username":["張三 "],"phone":[" 18812345678"]}
: kv轉化後引數:{"phone":["18812345678"],"username":["張三"]}
介面返回
張三&18812345678
說明首尾去空格成功!
請求url
http://127.0.0.1:8080/postTrim?username=張三 &phone= 18812345678
後臺輸出紀錄檔
: kv轉化前引數:{"username":["張三 "],"phone":[" 18812345678"]}
: kv轉化後引數:{"phone":["18812345678"],"username":["張三"]}
介面返回
張三&18812345678
說明首尾去空格成功!
請求url
http://127.0.0.1:8080/postJsonTrim
請求引數和返回引數
注意
這個請求頭為Content-Type:application/json
後臺輸出紀錄檔
json轉化前引數:{"phone":"18812345678 " ,"username":" 張三 "}
json轉化後引數:{"phone":"18812345678","username":"張三"}
說明首尾去空格成功!
專案範例原始碼
: https://github.com/yudiandemingzi/spring-boot-study
宣告: 公眾號如需轉載該篇文章,發表文章的頭部一定要 告知是轉至公眾號: 後端元宇宙。同時也可以問本人要markdown原稿和原圖片。其它情況一律禁止轉載!