`
maosheng
  • 浏览: 568067 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Spring MVC 基于 RequestBodyAdvice 和 ResponseBodyAdvice 全局处理输入输出

阅读更多
使用场景:

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 MVC 基于注解实例

    Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于...

    Spring MVC请求参数与响应结果全局加密和解密详解

    在本文中,我们将详细介绍Spring MVC请求参数与响应结果全局加密和解密的相关知识点,包括请求参数的加密和解密、响应结果的加密和解密、ContentType的处理等。 首先,让我们了解一下请求参数的加密和解密。为了...

    精通Spring MVC 4

    本书共计10章,分别介绍了快速搭建Spring Web应用、精通MVC结构、URL映射、文件上传与错误处理、创建Restful应用、保护应用、单元测试与验收测试、优化请求、将Web应用部署到云等内容,循序渐进地讲解了Spring MVC4...

    Spring MVC jar包

    Spring MVC 是一个基于Java的轻量级Web应用框架,它为开发者提供了模型-视图-控制器(MVC)架构,使开发人员能够更好地组织和分离应用程序的业务逻辑、数据处理和用户界面。Spring MVC是Spring框架的一个核心组件,...

    基于 Java ssh整合 开源博客系统 spring mvc,hibernate,spring,maven 整合开发

    【标题】中的“基于 Java ssh整合 开源博客系统”指的是一个使用Java技术栈开发的开源博客平台,这里的“ssh”是三个Java框架的缩写,分别代表Spring、Struts和Hibernate。Spring MVC、Hibernate和Spring是Java Web...

    最全最经典spring-mvc教程

    错误处理和异常处理也是Spring MVC中的重要部分,通过@ControllerAdvice和@ExceptionHandler可以全局处理异常,提供统一的错误页面。 最后,测试是任何应用程序开发的重要环节。Spring MVC提供了MockMVC,可以在不...

    Mastering Spring MVC 4(2015.09)源码

    首先,Spring MVC 4基于Servlet 3.0规范,这意味着它可以利用异步处理能力,提高了Web应用的性能。通过AsyncSupport和AsyncConfigurer接口,开发者可以轻松地创建异步控制器,处理高并发场景。 控制器(Controller...

    Spring MVC 教程快速入门 深入分析

    Spring MVC是一种基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,使用了IoC容器,支持RESTful风格的应用程序开发。Spring MVC通过分离模型(Model)、视图(View)和控制器(Controller)来简化Web开发...

    Spring MVC 学习笔记 九 json格式的输入和输出

    在这个学习笔记中,我们将深入探讨如何在Spring MVC中处理JSON格式的数据,包括输入和输出。JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,广泛应用于前后端数据传输。 1. **JSON格式简介** ...

    spring mvc 4.0

    8. **异步处理**:Spring MVC 4.0引入了异步请求处理,通过@ControllerAdvice和@Async注解,可以实现后台任务的异步执行,提高响应速度。 9. **多Part文件上传**:支持多文件上传,利用MultipartFile接口处理上传...

    Spring MVC 4.2.3

    它提供了模型-视图-控制器(MVC)架构,使开发者能够有效地分离业务逻辑、数据处理和用户界面。在"Spring MVC 4.2.3"版本中,我们看到了一系列的功能改进和优化,以提升开发效率和应用性能。 首先,让我们深入了解...

    Spring MVC跟jQuery和Twitter Bootstrap的结合

    例如,使用jQuery的`validate()`插件可以对表单输入进行验证,同时Spring MVC服务器端可以进行二次验证。 5. **异常处理**:Spring MVC通过`@ExceptionHandler`注解处理异常,可以返回定制的错误页面。配合jQuery,...

    Spring.MVC-A.Tutorial-Spring.MVC学习指南 高清可复制版PDF

    Spring MVC 是一个基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。这个教程“Spring MVC - A Tutorial”旨在帮助开发者深入理解和掌握Spring MVC的核心概念和...

    基于spring+spring mvc+hibernate的智能农业信息管理系统

    本文将深入探讨一款基于Spring、Spring MVC和Hibernate架构的智能农业信息管理系统,以及如何利用Spring Security进行安全防护。 首先,Spring作为Java领域中的轻量级框架,其核心特性是依赖注入(Dependency ...

    Spring MVC + Mybatis+Spring实现的个人博客系统

    这是一个基于Spring MVC、Mybatis和Spring框架实现的个人博客系统,涵盖了Web开发中的后端架构设计、数据库管理和前端展示等多个方面。以下将详细介绍这个系统的关键知识点: **1. Spring MVC** Spring MVC是Spring...

    开发Spring MVC应用程序补充—程序源码下载.rar_spring_spring mvc_spring mvc 源码_sp

    8. **异常处理**:配置和实现全局和局部异常处理器,确保优雅地处理错误和异常。 9. **单元测试**:利用MockMVC进行Controller层面的单元测试。 10. **源码分析**:通过对Spring MVC源码的阅读和理解,学习其内部...

    spring mvc 整合包

    Spring MVC 是一个强大的Java Web应用程序开发框架,是Spring框架的一部分,专注于处理Web请求和返回响应。它提供了模型-视图-控制器(MVC)架构,帮助开发者构建灵活、可维护的Web应用。在这个"spring mvc 整合包...

    Spring MVC入门教程

    十一、spring mvc 如何实现全局的异常处理? 十二、spring mvc 如何把全局异常记录到日志中? 十三、如何给spring3 MVC中的Action做JUnit单元测试? 十四、spring mvc 转发与重定向 十五、spring mvc 处理ajax请求 ...

    spring-MVC.zip_Java spring mvc_spring mvc_spring mvc

    Spring MVC 是一款基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。在本实例中,我们有一个名为"spring-MVC.zip"的压缩包,里面包含了一个关于Spring MVC的开发...

Global site tag (gtag.js) - Google Analytics