- 浏览: 565140 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (267)
- 随笔 (4)
- Spring (13)
- Java (61)
- HTTP (3)
- Windows (1)
- CI(Continuous Integration) (3)
- Dozer (1)
- Apache (11)
- DB (7)
- Architecture (41)
- Design Patterns (11)
- Test (5)
- Agile (1)
- ORM (3)
- PMP (2)
- ESB (2)
- Maven (5)
- IDE (1)
- Camel (1)
- Webservice (3)
- MySQL (6)
- CentOS (14)
- Linux (19)
- BI (3)
- RPC (2)
- Cluster (9)
- NoSQL (7)
- Oracle (25)
- Loadbalance (7)
- Web (5)
- tomcat (1)
- freemarker (1)
- 制造 (0)
最新评论
-
panamera:
如果设置了连接需要密码,Dynamic Broker-Clus ...
ActiveMQ 集群配置 -
panamera:
请问你的最后一种模式Broker-C节点是不是应该也要修改持久 ...
ActiveMQ 集群配置 -
maosheng:
longshao_feng 写道楼主使用 文件共享 模式的ma ...
ActiveMQ 集群配置 -
longshao_feng:
楼主使用 文件共享 模式的master-slave,produ ...
ActiveMQ 集群配置 -
tanglanwen:
感触很深,必定谨记!
少走弯路的十条忠告
使用场景:
1.需要对项目中的所有输入进行前后空格的过滤
2.替换一些特殊字符的输入
3.解密一些关键性字段
4.注入一些参数在请求方法的时候
5.返回参数统一处理,如果后台返回空,统一返回成功信息
6.身份证等特殊字符统一做 * 号处理等
Code:
主要就是用到了 RequestBodyAdvice 和 ResponseBodyAdvice 两个接口和一个注解@ControllerAdvice
RequestBodyAdvice:在 sping 4.2 新加入的一个接口,它可以使用在 @RequestBody 修改的参数之前进行参数的处理,比如进行参数的解密。
ResponseBodyAdvice:在 spring 4.1 新加入的一个接口,在消息体被HttpMessageConverter写入之前允许 Controller 中 @ResponseBody 修饰的方法调整响应中的内容,比如进行相应的加密。
SpringBoot 中可以利用@RequestBody这样的注解完成请求内容体与对象的转换。而RequestBodyAdvice 则可用于在请求内容对象转换的前后时刻进行拦截处理,其定义了几个方法:
supports:判断是否支持handleEmptyBody,当请求体为空时调用
beforeBodyRead:在请求体未读取(转换)时调用
afterBodyRead:在请求体完成读取后调用
SpringMVC对出参和入参有非常友好的拓展支持,方便你对数据的输入和输出有更大的执行权,我们如何通过SpringMVC定义的结果做一系列处理呢?
RequestBodyAdvice:
入参,针对所有以@RequestBody的参数做处理
ResponseBodyAdvice:
出参,针对所有以@ResponseBody的参数做处理
@ControllerAdvice
public class LogResponseBodyAdvice implements ResponseBodyAdvice {
/**
*
* @param returnType
* @param converterType
* @return
*/
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
// 做任何事情 body 就是返回的结果对象,没有处理之前
return body;
}
}
注意事项: 自定义的处理对象类上必须得加上@ControllerAdvice注解!
为什么?
源码中RequestMappingHandlerAdapter类在执行initControllerAdviceCache()做初始化的时候会执行一个:
List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
AnnotationAwareOrderComparator.sort(beans);
而ControllerAdviceBean.findAnnotatedBeans方法会查找类上有ControllerAdvice注解的类才会加入到处理当中:
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext applicationContext) {
List<ControllerAdviceBean> beans = new ArrayList<ControllerAdviceBean>();
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class)) {
if (applicationContext.findAnnotationOnBean(name, ControllerAdvice.class) != null) {
beans.add(new ControllerAdviceBean(name, applicationContext));
}
}
return beans;
}
所以大家可以根据自己的需要,定义结果的入参和出参结果做一些特殊处理。
请求参数去空格:
/**
* 去掉前后空格和特殊字符
*/
@Slf4j
@ControllerAdvice
public class CustomRequestBodyAdvice implements RequestBodyAdvice {
@Override
public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return body;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
return new CustomHttpInputMessage(httpInputMessage);
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return body;
}
class CustomHttpInputMessage implements HttpInputMessage{
private HttpInputMessage origin;
public CustomHttpInputMessage(HttpInputMessage httpInputMessage) {
this.origin = httpInputMessage;
}
@Override
public InputStream getBody() throws IOException {
HttpHeaders headers = origin.getHeaders();
InputStream body = origin.getBody();
// 空参,get 请求,流为空,非 application/json 请求,不处理参数
MediaType contentType = headers.getContentType();
if(contentType == null){return body;}
if(!contentType.isCompatibleWith(MediaType.APPLICATION_JSON)){return body;}
if(body == null){return body;}
String params = IOUtils.toString(body, "utf-8");
if(StringUtils.isBlank(params)){return body;}
// 正式过滤 json 参数
Object parse = JSON.parse(params);
if (parse instanceof JSONArray) {
JSONArray jsonArray = (JSONArray) parse;
trimJsonArray(jsonArray);
} else if (parse instanceof JSONObject) {
trimJsonObject((JSONObject) parse);
} else {
log.error("参数不支持去空格:" + parse+ " contentType:"+contentType);
}
return IOUtils.toInputStream(JSON.toJSONString(parse, SerializerFeature.WriteMapNullValue), "UTF-8");
}
private void trimJsonObject(JSONObject jsonObject) {
Iterator<Map.Entry<String, Object>> iterator = jsonObject.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Object> next = iterator.next();
String key = next.getKey();
Object value = next.getValue();
if (value instanceof JSONArray) {
trimJsonArray((JSONArray) value);
}else if(value instanceof JSONObject){
trimJsonObject((JSONObject) value);
}else if(value instanceof String){
String trimValue = StringUtils.trim(ObjectUtils.toString(value));
next.setValue(filterDangerString(trimValue));
}
}
}
private void trimJsonArray(JSONArray jsonArray) {
for (int i = 0; i < jsonArray.size(); i++) {
Object object = jsonArray.get(i);
if(object instanceof JSONObject){
JSONObject jsonObject = jsonArray.getJSONObject(i);
trimJsonObject(jsonObject);
}else if(object instanceof String){
String trimValue = StringUtils.trim(ObjectUtils.toString(object));
jsonArray.set(i,trimValue);
}
}
}
@Override
public HttpHeaders getHeaders() {
return origin.getHeaders();
}
private String filterDangerString(String value) {
if(StringUtils.isBlank(value))return value;
value = value.replaceAll(";", ";");
value = value.replaceAll("'", "‘");
value = value.replaceAll("<", "《");
value = value.replaceAll(">", "》");
value = value.replaceAll("\\(", "(");
value = value.replaceAll("\\)", ")");
value = value.replaceAll("\\?", "?");
return value;
}
}
}
各种拦截器接口的执行顺序:
Filter ---> HandlerInterceptor ---> RequestBodyAdvice ---> @Aspect.Pointcut ---> Controller.body
@RestControllerAdvice和@ControllerAdvice 在具体使用上:
1)注解有@ControllerAdvice的类, 需要在具体方法上同时添加@ExceptionHandler和@ResponseBody注解;
2)注解有@RestControllerAdvice的类,只需要在具体方法上添加@ExceptionHandler注解。
注意:在 Spring MVC 中,ServletRequest中getReader()和getInputStream()只能调用一次(Java中input流只能读取一次,主要原因是通标记的方法来判断流是否读取完毕,读取位 -1就是流读取完毕)。而又由于@RequestBody注解获取输出参数的方式也是根据流的方式获取的。所以如果我们前面使用流获取后,后面的@RequestBody就获取不到对应的输入流了。
解决思路就是:先读取流,然后在将流写回去(将流赋值给一个 byte[] 数组,下面读取流时就调用这个数组就行)
1.需要对项目中的所有输入进行前后空格的过滤
2.替换一些特殊字符的输入
3.解密一些关键性字段
4.注入一些参数在请求方法的时候
5.返回参数统一处理,如果后台返回空,统一返回成功信息
6.身份证等特殊字符统一做 * 号处理等
Code:
主要就是用到了 RequestBodyAdvice 和 ResponseBodyAdvice 两个接口和一个注解@ControllerAdvice
RequestBodyAdvice:在 sping 4.2 新加入的一个接口,它可以使用在 @RequestBody 修改的参数之前进行参数的处理,比如进行参数的解密。
ResponseBodyAdvice:在 spring 4.1 新加入的一个接口,在消息体被HttpMessageConverter写入之前允许 Controller 中 @ResponseBody 修饰的方法调整响应中的内容,比如进行相应的加密。
SpringBoot 中可以利用@RequestBody这样的注解完成请求内容体与对象的转换。而RequestBodyAdvice 则可用于在请求内容对象转换的前后时刻进行拦截处理,其定义了几个方法:
supports:判断是否支持handleEmptyBody,当请求体为空时调用
beforeBodyRead:在请求体未读取(转换)时调用
afterBodyRead:在请求体完成读取后调用
SpringMVC对出参和入参有非常友好的拓展支持,方便你对数据的输入和输出有更大的执行权,我们如何通过SpringMVC定义的结果做一系列处理呢?
RequestBodyAdvice:
入参,针对所有以@RequestBody的参数做处理
ResponseBodyAdvice:
出参,针对所有以@ResponseBody的参数做处理
@ControllerAdvice
public class LogResponseBodyAdvice implements ResponseBodyAdvice {
/**
*
* @param returnType
* @param converterType
* @return
*/
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
// 做任何事情 body 就是返回的结果对象,没有处理之前
return body;
}
}
注意事项: 自定义的处理对象类上必须得加上@ControllerAdvice注解!
为什么?
源码中RequestMappingHandlerAdapter类在执行initControllerAdviceCache()做初始化的时候会执行一个:
List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
AnnotationAwareOrderComparator.sort(beans);
而ControllerAdviceBean.findAnnotatedBeans方法会查找类上有ControllerAdvice注解的类才会加入到处理当中:
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext applicationContext) {
List<ControllerAdviceBean> beans = new ArrayList<ControllerAdviceBean>();
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class)) {
if (applicationContext.findAnnotationOnBean(name, ControllerAdvice.class) != null) {
beans.add(new ControllerAdviceBean(name, applicationContext));
}
}
return beans;
}
所以大家可以根据自己的需要,定义结果的入参和出参结果做一些特殊处理。
请求参数去空格:
/**
* 去掉前后空格和特殊字符
*/
@Slf4j
@ControllerAdvice
public class CustomRequestBodyAdvice implements RequestBodyAdvice {
@Override
public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return body;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
return new CustomHttpInputMessage(httpInputMessage);
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return body;
}
class CustomHttpInputMessage implements HttpInputMessage{
private HttpInputMessage origin;
public CustomHttpInputMessage(HttpInputMessage httpInputMessage) {
this.origin = httpInputMessage;
}
@Override
public InputStream getBody() throws IOException {
HttpHeaders headers = origin.getHeaders();
InputStream body = origin.getBody();
// 空参,get 请求,流为空,非 application/json 请求,不处理参数
MediaType contentType = headers.getContentType();
if(contentType == null){return body;}
if(!contentType.isCompatibleWith(MediaType.APPLICATION_JSON)){return body;}
if(body == null){return body;}
String params = IOUtils.toString(body, "utf-8");
if(StringUtils.isBlank(params)){return body;}
// 正式过滤 json 参数
Object parse = JSON.parse(params);
if (parse instanceof JSONArray) {
JSONArray jsonArray = (JSONArray) parse;
trimJsonArray(jsonArray);
} else if (parse instanceof JSONObject) {
trimJsonObject((JSONObject) parse);
} else {
log.error("参数不支持去空格:" + parse+ " contentType:"+contentType);
}
return IOUtils.toInputStream(JSON.toJSONString(parse, SerializerFeature.WriteMapNullValue), "UTF-8");
}
private void trimJsonObject(JSONObject jsonObject) {
Iterator<Map.Entry<String, Object>> iterator = jsonObject.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Object> next = iterator.next();
String key = next.getKey();
Object value = next.getValue();
if (value instanceof JSONArray) {
trimJsonArray((JSONArray) value);
}else if(value instanceof JSONObject){
trimJsonObject((JSONObject) value);
}else if(value instanceof String){
String trimValue = StringUtils.trim(ObjectUtils.toString(value));
next.setValue(filterDangerString(trimValue));
}
}
}
private void trimJsonArray(JSONArray jsonArray) {
for (int i = 0; i < jsonArray.size(); i++) {
Object object = jsonArray.get(i);
if(object instanceof JSONObject){
JSONObject jsonObject = jsonArray.getJSONObject(i);
trimJsonObject(jsonObject);
}else if(object instanceof String){
String trimValue = StringUtils.trim(ObjectUtils.toString(object));
jsonArray.set(i,trimValue);
}
}
}
@Override
public HttpHeaders getHeaders() {
return origin.getHeaders();
}
private String filterDangerString(String value) {
if(StringUtils.isBlank(value))return value;
value = value.replaceAll(";", ";");
value = value.replaceAll("'", "‘");
value = value.replaceAll("<", "《");
value = value.replaceAll(">", "》");
value = value.replaceAll("\\(", "(");
value = value.replaceAll("\\)", ")");
value = value.replaceAll("\\?", "?");
return value;
}
}
}
各种拦截器接口的执行顺序:
Filter ---> HandlerInterceptor ---> RequestBodyAdvice ---> @Aspect.Pointcut ---> Controller.body
@RestControllerAdvice和@ControllerAdvice 在具体使用上:
1)注解有@ControllerAdvice的类, 需要在具体方法上同时添加@ExceptionHandler和@ResponseBody注解;
2)注解有@RestControllerAdvice的类,只需要在具体方法上添加@ExceptionHandler注解。
注意:在 Spring MVC 中,ServletRequest中getReader()和getInputStream()只能调用一次(Java中input流只能读取一次,主要原因是通标记的方法来判断流是否读取完毕,读取位 -1就是流读取完毕)。而又由于@RequestBody注解获取输出参数的方式也是根据流的方式获取的。所以如果我们前面使用流获取后,后面的@RequestBody就获取不到对应的输入流了。
解决思路就是:先读取流,然后在将流写回去(将流赋值给一个 byte[] 数组,下面读取流时就调用这个数组就行)
发表评论
-
Spring Cloud 微服务异常处理机制
2020-04-27 15:43 0在基于SpringCloud的微服务架构中,所有服务都是通过如 ... -
SpringCloud Ribbon的重试和Hystrix的超时时间配置
2020-04-27 15:27 940先看下Ribbon的配置和Hystrix的超时配置: ribb ... -
SpringCloud Hystrix实现服务熔断和服务降级
2020-02-17 15:06 674一.雪崩效应 (1)微服 ... -
Spring Boot集成dubbo,mybatis构建maven工程
2017-07-14 10:54 1189一.Dubbo服务提供者(Spring Boot+Dubbo+ ... -
深入学习微框架:Spring Boot
2016-01-19 16:52 1631Spring Boot是由Pivotal团队 ... -
使用Spring Boot 创建微服务
2016-01-19 16:48 1053过去几年以来,“微服 ... -
基于CGLib(面向Class)的Spring AOP实现原理
2013-07-17 16:47 1476Spring中,基于Dynamic Proxy是面向接口的动态 ... -
基于Java Dynamic Proxy(面向Interface)的Spring AOP实现原理
2013-07-17 16:23 1519Dynamic Proxy是面向接口的动态代理实现,其代理对象 ... -
Spring JdbcTemplate源码解析
2013-07-16 17:21 4551在Spring中,JdbcTemplate是经常被使用的类来帮 ... -
Spring 基于注解驱动的MVC
2012-03-16 17:18 1293继 Spring 2.0 对 Spring MVC 进行重大升 ... -
Hard code获取Spring中Bean的几种方法
2012-01-05 14:01 1398方法一:(通过FileSystemXmlApplication ... -
配置文件加载Spring配置文件方法
2012-01-05 13:39 1113方法一:(web.xml)<web-app> ... -
反射实现AOP 动态代理模式(Spring AOP 的实现原理)
2011-12-22 17:24 1171我们先写一个接口IHello.java代码如下: 1 pa ...
相关推荐
Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于...
在本文中,我们将详细介绍Spring MVC请求参数与响应结果全局加密和解密的相关知识点,包括请求参数的加密和解密、响应结果的加密和解密、ContentType的处理等。 首先,让我们了解一下请求参数的加密和解密。为了...
本书共计10章,分别介绍了快速搭建Spring Web应用、精通MVC结构、URL映射、文件上传与错误处理、创建Restful应用、保护应用、单元测试与验收测试、优化请求、将Web应用部署到云等内容,循序渐进地讲解了Spring MVC4...
Spring MVC 是一个基于Java的轻量级Web应用框架,它为开发者提供了模型-视图-控制器(MVC)架构,使开发人员能够更好地组织和分离应用程序的业务逻辑、数据处理和用户界面。Spring MVC是Spring框架的一个核心组件,...
【标题】中的“基于 Java ssh整合 开源博客系统”指的是一个使用Java技术栈开发的开源博客平台,这里的“ssh”是三个Java框架的缩写,分别代表Spring、Struts和Hibernate。Spring MVC、Hibernate和Spring是Java Web...
错误处理和异常处理也是Spring MVC中的重要部分,通过@ControllerAdvice和@ExceptionHandler可以全局处理异常,提供统一的错误页面。 最后,测试是任何应用程序开发的重要环节。Spring MVC提供了MockMVC,可以在不...
首先,Spring MVC 4基于Servlet 3.0规范,这意味着它可以利用异步处理能力,提高了Web应用的性能。通过AsyncSupport和AsyncConfigurer接口,开发者可以轻松地创建异步控制器,处理高并发场景。 控制器(Controller...
Spring MVC是一种基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,使用了IoC容器,支持RESTful风格的应用程序开发。Spring MVC通过分离模型(Model)、视图(View)和控制器(Controller)来简化Web开发...
在这个学习笔记中,我们将深入探讨如何在Spring MVC中处理JSON格式的数据,包括输入和输出。JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,广泛应用于前后端数据传输。 1. **JSON格式简介** ...
8. **异步处理**:Spring MVC 4.0引入了异步请求处理,通过@ControllerAdvice和@Async注解,可以实现后台任务的异步执行,提高响应速度。 9. **多Part文件上传**:支持多文件上传,利用MultipartFile接口处理上传...
它提供了模型-视图-控制器(MVC)架构,使开发者能够有效地分离业务逻辑、数据处理和用户界面。在"Spring MVC 4.2.3"版本中,我们看到了一系列的功能改进和优化,以提升开发效率和应用性能。 首先,让我们深入了解...
例如,使用jQuery的`validate()`插件可以对表单输入进行验证,同时Spring MVC服务器端可以进行二次验证。 5. **异常处理**:Spring MVC通过`@ExceptionHandler`注解处理异常,可以返回定制的错误页面。配合jQuery,...
Spring MVC 是一个基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。这个教程“Spring MVC - A Tutorial”旨在帮助开发者深入理解和掌握Spring MVC的核心概念和...
本文将深入探讨一款基于Spring、Spring MVC和Hibernate架构的智能农业信息管理系统,以及如何利用Spring Security进行安全防护。 首先,Spring作为Java领域中的轻量级框架,其核心特性是依赖注入(Dependency ...
8. **异常处理**:配置和实现全局和局部异常处理器,确保优雅地处理错误和异常。 9. **单元测试**:利用MockMVC进行Controller层面的单元测试。 10. **源码分析**:通过对Spring MVC源码的阅读和理解,学习其内部...
Spring MVC 是一个强大的Java Web应用程序开发框架,是Spring框架的一部分,专注于处理Web请求和返回响应。它提供了模型-视图-控制器(MVC)架构,帮助开发者构建灵活、可维护的Web应用。在这个"spring mvc 整合包...
十一、spring mvc 如何实现全局的异常处理? 十二、spring mvc 如何把全局异常记录到日志中? 十三、如何给spring3 MVC中的Action做JUnit单元测试? 十四、spring mvc 转发与重定向 十五、spring mvc 处理ajax请求 ...
通过学习和实践这个demo,你可以理解Spring MVC的基本工作原理,掌握如何创建Controller、配置DispatcherServlet、处理请求和响应,以及如何利用Maven管理项目构建。这将为你进一步深入Spring框架和Java Web开发打下...
Spring MVC 是一款基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。在本实例中,我们有一个名为"spring-MVC.zip"的压缩包,里面包含了一个关于Spring MVC的开发...