背景 : 用户在前端页面中不小心输入的前后空格,为了防止因为前后空格原因引起业务异常,所以我们需要去除参数的前后空格!

如果我们手动去除参数前后空格,我们可以这样做

    @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注解是无效的。

既然是通过过滤器获取请求参数去除参数的首尾空格,那我们应该考虑几种情况的请求参数

  1. Get请求,请求参数放到url后面
  2. Post请求 请求参数放到url后面
  3. Post请求 请求参数放到body里面

第一种和第二种其实可以通过一种方式实现就是request.getParameter()方法。

Post中body请求参数我们可以通过使用流的方式,调用request.getInputStream()获取流,然后从流中读取参数。

一、实现代码

1、注册过滤器

/**
* 通过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;
}
}

2、自定义过滤器TrimFilter

/**
* 自定义过滤器 通过继承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);
}
}

3、自定义TrimRequestWrapper类

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);
}
}

1、Get请求测试首尾去空格

请求url

http://localhost:8080/getTrim?username=张三 &phone= 18812345678

后台输出日志

: kv转化前参数:{"username":["张三 "],"phone":[" 18812345678"]}
: kv转化后参数:{"phone":["18812345678"],"username":["张三"]}

接口返回

张三&18812345678

说明首尾去空格成功!

2、Post方法测试首尾去空格

请求url

http://127.0.0.1:8080/postTrim?username=张三 &phone= 18812345678

后台输出日志

: kv转化前参数:{"username":["张三 "],"phone":[" 18812345678"]}
: kv转化后参数:{"phone":["18812345678"],"username":["张三"]}

接口返回

张三&18812345678

说明首尾去空格成功!

3、post方法 json参数测试首尾去空格

请求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原稿和原图片。其它情况一律禁止转载!

springBoot 过滤器去除请求参数前后空格(附源码)的更多相关文章

  1. SpringBoot获取http请求参数的方法

    SpringBoot获取http请求参数的方法 原文:https://www.cnblogs.com/zhanglijun/p/9403483.html 有七种Java后台获取前端传来参数的方法,稍微 ...

  2. 【SpringCloud】Gateway 配置全局过滤器获取请求参数和响应值

    [SpringCloud]Gateway 配置全局过滤器获取请求参数和响应值 实现Ordered接口getOrder()方法,数值越小越靠前执行,记得这一点就OK了. 获取请求参数RequestBod ...

  3. 详解SpringBoot集成jsp(附源码)+遇到的坑

    本文介绍了SpringBoot集成jsp(附源码)+遇到的坑 ,分享给大家 1.大体步骤 (1)创建Maven web project: (2)在pom.xml文件添加依赖: (3)配置applica ...

  4. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  5. C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)

    前言:之前的两篇封装了一些基础的表单组件,这篇继续来封装几个基于bootstrap的其他组件.和上篇不同的是,这篇的有几个组件需要某些js文件的支持. 本文原创地址:http://www.cnblog ...

  6. 使用MiniProfiler给Asp.net MVC和Entity Framework号脉(附源码)

    在学习python开发框架pylons/pyramid的过程中,里面有个非常棒的页面性能监控功能,这样在开发过程中,你能清楚的知道当前页面的性能以及其它参数. 这里介绍一下如何给Asp.net MVC ...

  7. 聊天系统Demo,增加文件传送功能(附源码)-- ESFramework 4.0 快速上手(14)

    本文我们将介绍在ESFramework 4.0 快速上手(08) -- 入门Demo,一个简单的IM系统(附源码)的基础上,增加文件传送的功能.如果不了解如何使用ESFramework提供的文件传送功 ...

  8. ArcGIS紧凑型切片读取与应用2-webgis动态加载紧凑型切片(附源码)

    1.前言 上篇主要讲了一下紧凑型切片的的解析逻辑,这一篇主要讲一下使用openlayers动态加载紧凑型切片的web地图服务. 2.代码实现 上篇已经可以通过切片的x.y.z得对应的切片图片,现在使用 ...

  9. 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生

    [转].NET(C#):浅谈程序集清单资源和RESX资源   目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...

  10. leaflet结合geoserver利用WFS服务实现图层删除功能(附源码下载)

    前言 leaflet 入门开发系列环境知识点了解: leaflet api文档介绍,详细介绍 leaflet 每个类的函数以及属性等等 leaflet 在线例子 leaflet 插件,leaflet ...

随机推荐

  1. 点赞和取消点赞实现Redis缓存(只思路)

    思路:点赞.取消点赞 --> Redis --> (每两个小时)存到数据库(MySQL),所以就相当于每次查询或者存储都需要先经过Redis,而查询的目的是为了判断用户的点赞状态(已点赞o ...

  2. 使用mtr来判断网络丢包和网络延迟

    转载自:https://mp.weixin.qq.com/s/UsjzMS1_rdxenw0TPlqwyQ 常用的 ping,tracert,nslookup 一般用来判断主机的网络连通性,其实 Li ...

  3. 路径参数和数值校验: Path_Parameters_and_Numeric_Validations

    官方文档地址: https://fastapi.tiangolo.com/zh/tutorial/path-params-numeric-validations/ # -*- coding: UTF- ...

  4. Linux实例常用内核网络参数介绍与常见问题处理---重要

    文章转载自:https://help.aliyun.com/knowledge_detail/41334.html 本文主要介绍如下几点内容,您可以根据实际需要选择. 查看和修改Linux实例内核参数 ...

  5. 《Vision Permutator: A Permutable MLP-Like ArchItecture For Visual Recognition》论文笔记

    论文题目:<Vision Permutator: A Permutable MLP-Like ArchItecture For Visual Recognition> 论文作者:Qibin ...

  6. PAT (Basic Level) Practice 1023 组个最小数 分数 20

    给定数字 0-9 各若干个.你可以以任意顺序排列这些数字,但必须全部使用.目标是使得最后得到的数尽可能小(注意 0 不能做首位).例如:给定两个 0,两个 1,三个 5,一个 8,我们得到的最小的数就 ...

  7. day04-MySQL常用函数01

    5.MySQL常用函数 5.1合计/统计函数 5.1.1合计函数-count count 返回行的总数 Select count(*)|count (列名) from table_name [WHER ...

  8. 矩阵顺时针打印(C++)(? 为什么不能AC,9度1391)

    #include <iostream> #include <fstream> using namespace std; int a[1000][1000]; void prin ...

  9. ExcelHelper ->NPOI插件

    调用: var ms = ExcelHelper.PrintInvoiceToExcel("观看时长", headline, items, (row, item) => { ...

  10. 通过openlayers加载dwg格式的CAD图并与互联网地图叠加

    Openlayers介绍 ​ Openlayers是一个基于Javacript开发,免费.开源的前端地图开发库,使用它,可以很容易的开发出WebGIS系统.目前Openlayers支持地图瓦片.矢量数 ...