项目中统一处理请求中的字符串参数的空格
测试controller
@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {
@GetMapping("/testStringParamTrim")
public TestObjectInfo testStringParamTrim(@RequestParam String goodsId, String goodsName) {
return new TestObjectInfo().setGoodsId(goodsId).setGoodsName(goodsName);
}
@GetMapping("/testObjectTrim")
public TestObjectInfo testObjectTrim(TestObjectInfo objectInfo) {
return objectInfo;
}
@PostMapping("/testBodyObjectTrim")
public TestObjectInfo testBodyObjectTrim(@RequestBody TestObjectInfo objectInfo) {
return objectInfo;
}
@Data
@Accessors(chain = true)
public static class TestObjectInfo {
private String goodsId;
private String goodsName;
}
}
测试去除 GET请求参数 和 POST请求体 的属性值的前后空格。
处理请求参数
@Configuration
public class WebConfig {
@Autowired
public void setWebBindingInitializer(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
WebBindingInitializer webBindingInitializer = requestMappingHandlerAdapter.getWebBindingInitializer();
if (webBindingInitializer instanceof ConfigurableWebBindingInitializer) {
//添加属性编辑器注册器,注册String类型的属性编辑器
PropertyEditorRegistrar[] propertyEditorRegistrars = ((ConfigurableWebBindingInitializer) webBindingInitializer).getPropertyEditorRegistrars();
if (Objects.isNull(propertyEditorRegistrars)) {
propertyEditorRegistrars = new PropertyEditorRegistrar[0];
}
int originalLen = propertyEditorRegistrars.length;
propertyEditorRegistrars = Arrays.copyOf(propertyEditorRegistrars, originalLen + 1);
propertyEditorRegistrars[originalLen] = new CustomPropertyEditorRegistrar();
((ConfigurableWebBindingInitializer) webBindingInitializer).setPropertyEditorRegistrars(propertyEditorRegistrars);
}
}
public static class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(String.class, new StringTrimmerEditor(false));
}
}
}
通过扩展 SpringMVC 中的 PropertyEditorRegistrar 来实现,这种方式可以实现将请求参数中的字符串参数都去除前后空格,但有一定的隐患,不确定是否会影响项目中 Spring 的其他使用场景。
处理请求体
@Component
public class CustomerJackson2ObjectMapperBuilderCustomizer implements Jackson2ObjectMapperBuilderCustomizer {
@Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
jacksonObjectMapperBuilder.deserializerByType(String.class, new JsonDeserializer<String>() {
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String value = jsonParser.getValueAsString();
if (Objects.nonNull(value)) {
return value.trim();
}
return value;
}
});
}
}
这种方式通过 自定义 jackson 反序列化字符串时的逻辑,来去除前后空格,但限制了 jackson,不支持 fastjson 或者其他 json 框架且影响项目中其他的序列化场景。
更好的方式
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean<ParamsFilter> paramsFilter() {
FilterRegistrationBean<ParamsFilter> registration = new FilterRegistrationBean();
registration.setFilter(new ParamsFilter());
registration.addUrlPatterns("/*");
registration.setName("paramsFilter");
return registration;
}
public static class ParamsFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
ParameterRequestWrapper paramRequest = new ParameterRequestWrapper(request);
filterChain.doFilter(paramRequest, response);
}
}
@Slf4j
public static class ParameterRequestWrapper extends HttpServletRequestWrapper {
private final Map<String, String[]> params = new HashMap<>();
public ParameterRequestWrapper(HttpServletRequest request) {
// 将request交给父类,以便于调用对应方法的时候,将其输出,其实父亲类的实现方式和第一种new的方式类似
super(request);
//将参数表,赋予给当前的Map以便于持有request中的参数
Map<String, String[]> requestMap = request.getParameterMap();
this.params.putAll(requestMap);
log.info("转化前请求参数:{}", JSON.toJSONString(params));
this.modifyParameterValues();
log.info("转化后请求参数:{}", JSON.toJSONString(params));
}
/**
* 重写getInputStream方法 post类型的请求参数必须通过流才能获取到值
*/
@Override
public ServletInputStream getInputStream() throws IOException {
//非json类型,直接返回
String contentType = super.getHeader(HttpHeaders.CONTENT_TYPE);
if (StringUtils.isEmpty(contentType) || !contentType.startsWith(MediaType.APPLICATION_JSON_VALUE)) {
return super.getInputStream();
}
String json = IOUtils.toString(super.getInputStream(), StandardCharsets.UTF_8);
log.info("转化前请求体:{}", json);
Map<String, Object> map = StringJsonUtils.jsonStringToMap(json);
log.info("转化后请求体:{}", JSON.toJSONString(map));
ByteArrayInputStream bis = new ByteArrayInputStream(JSON.toJSONString(map).getBytes(StandardCharsets.UTF_8));
return new MyServletInputStream(bis);
}
/**
* 将parameter的值去除空格后重写回去
*/
private void modifyParameterValues() {
Map<String, String[]> newParams = new HashMap<>();
for (Map.Entry<String, String[]> entry : params.entrySet()) {
String key = entry.getKey();
String[] values = entry.getValue();
if (Objects.nonNull(values) && values.length > 0) {
values[0] = values[0].trim();
}
newParams.put(key, values);
}
params.putAll(newParams);
}
/**
* 重写getParameter 参数从当前类中的map获取
*/
@Override
public String getParameter(String name) {
String[] values = params.get(name);
if (Objects.nonNull(values) && values.length > 0) {
return values[0];
}
return null;
}
/**
* 重写getParameterValues
*/
@Override
public String[] getParameterValues(String name) {//同上
return params.get(name);
}
public static class MyServletInputStream extends ServletInputStream {
private ByteArrayInputStream bis;
public MyServletInputStream(ByteArrayInputStream bis) {
this.bis = bis;
}
@Override
public boolean isFinished() {
return true;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() throws IOException {
return bis.read();
}
}
}
}
public class StringJsonUtils {
/**
* jsonstring 转换成map,并去除字符串值的前后空字符串--支持嵌套 k-v v=list
*
* @param jsonString 必须是 {}对象
* @return
*/
public static Map<String, Object> jsonStringToMap(String jsonString) {
Map<String, Object> map = JSON.parseObject(jsonString);
for (Map.Entry<String, Object> entry : map.entrySet()) {
Object value = entry.getValue();
if (Objects.isNull(value)) {
continue;
}
if (value instanceof String) {
entry.setValue(((String) value).trim());
} else if (value instanceof Map) {
Map<String, Object> stringObjectMap = jsonStringToMap(JSON.toJSONString(value));
entry.setValue(stringObjectMap);
} else if (value instanceof List) {
List<Object> stringObjects = stringListTrim(JSON.toJSONString(value));
entry.setValue(stringObjects);
}
}
return map;
}
/**
* List中的字符串去除空格
*
* @param jsonString
*/
private static List<Object> stringListTrim(String jsonString) {
JSONArray objects = JSONArray.parseArray(jsonString);
List<Object> newObjects = new ArrayList<>();
for (Object object : objects) {
if (Objects.isNull(object)) {
continue;
}
if (object instanceof String) {
newObjects.add(((String) object).trim());
} else if (object instanceof Map) {
Map<String, Object> stringObjectMap = jsonStringToMap(JSON.toJSONString(object));
newObjects.add(stringObjectMap);
} else if (object instanceof List) {
List<Object> stringObjects = stringListTrim(JSON.toJSONString(object));
newObjects.add(stringObjects);
} else {
newObjects.add(object);
}
}
return newObjects;
}
}
通过一个 过滤器,来统一处理请求参数和请求体,且不耦合 Spring 及 具体的 json 框架。