目录

背景

使用场景

接口说明

RequestBodyAdvice

ResponseBodyAdvice

示例

场景说明:参数转换

自定义注解

User实体对象

接口返回对象

自定义RequestBodyAdvice参数处理器

自定义ResponseBodyAdvice返回值处理器

controller接口

测试

注意事项


RequestBodyAdvice

        在执行Controller接口之前,如果Controller参数是被@RequestBody修饰,可以对该参数进行一些特殊处理,比如参数解密,参数转换等。

ResponseBodyAdvice

        执行完Controller接口之后,在response返回给客户端之前,如果Controller方法被@ResponseBody修饰,此时可以对response返回的数据做一些特殊处理,比如返回值加密,构造统一返回对象,返回值转换等。

背景

在SpringBoot项目中,很多时候都需要对Controller层请求参数和响应结果做一些公共操作,这时就可以使用RequestBodyAdvice和ResponseBodyAdvice。

使用场景

对接口参数进行前后空格的过滤;

参数转换,比如字段关联字典:将字典显示值转换为保存值;

解密一些关键性字段;

返回参数统一处理;

身份证等特殊字符统一转换为*;

接口说明

RequestBodyAdvice

public interface RequestBodyAdvice {

    /**
     * 该方法用于判断当前请求,是否要执行beforeBodyRead方法
     * methodParameter方法的参数对象
     * type方法的参数类型
     * aClass 将会使用到的Http消息转换器类类型
     * 注意:此判断方法,会在beforeBodyRead 和 afterBodyRead方法前都触发一次。
     *
     * @return 返回true则会执行beforeBodyRead
     */
    boolean supports(MethodParameter methodParameter, Type targetType,
                     Class<? extends HttpMessageConverter<?>> converterType);

    /**
     * 在Http消息转换器执转换,之前执行
     * inputMessage 客户端的请求数据
     * parameter方法的参数对象
     * targetType方法的参数类型
     * converterType 将会使用到的Http消息转换器类类型
     * @return 返回 一个自定义的HttpInputMessage
     */
    HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
                                    Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException;

    /**
     * 在Http消息转换器执转换,之后执行
     * body 转换后的对象
     * inputMessage 客户端的请求数据
     * parameter 方法的参数类型
     * targetType 方法的参数类型
     * converterType 使用的Http消息转换器类类型
     * @return 返回一个新的对象
     */
    Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
                         Type targetType, Class<? extends HttpMessageConverter<?>> converterType);

    /**
     * 参数与afterBodyRead相同,body为空时由该方法处理
     */
    @Nullable
    Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
                           Type targetType, Class<? extends HttpMessageConverter<?>> converterType);


}

ResponseBodyAdvice

public interface ResponseBodyAdvice<T> {

    /**
     * 判断当前返回结果,是否要执行beforeBodyWrite方法
     * @param returnType
     * @param converterType
     * @return
     */
    boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);

    /**
     * 对返回对象做一些特殊处理
     * @param body  返回给前端的对象
     * @param returnType    控制器方法的返回类型
     * @param selectedContentType   内容类型
     * @param selectedConverterType 选择器类型
     * @param request     请求对象
     * @param response    返回值对象
     * @return
     */
    @Nullable
    T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
                      Class<? extends HttpMessageConverter<?>> selectedConverterType,
                      ServerHttpRequest request, ServerHttpResponse response);

}

示例

场景说明:参数转换

        自定义一个注解@TransParam,现在需要将Controller接口中,@TransParam注解修饰的参数进行参数转换。

        转换关系:controller入参:男->0,女->1,其他值->2;

        转换关系:controller出参:0->男1,1->女1,其他值->中性

自定义注解

注解可应用于

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TransParam {
}

User实体对象

User对象中sex字段被@TransParam修饰。

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
    private String name;

    @TransParam 
    private int sex;
}

接口返回对象

@Data
@AllArgsConstructor
public class ResponseResult {
    private int code;
    private Object content;

    public static ResponseResult fail(Object content) {
        return new ResponseResult(HttpStatus.FOUND.value(), content);
    }

    public static ResponseResult success(Object content) {
        return new ResponseResult(HttpStatus.OK.value(), content);
    }
}

自定义RequestBodyAdvice参数处理器

作用:将@TransParam修饰的字段值,进行转换:男->0,女->1,其他值->2;

主要会用到supports和beforeBodyRead两个方法:

supports判断是否对当前接口进行参数转换;

beforeBodyRead执行具体的转换逻辑。

@ControllerAdvice
public class MyRequestBodyAdvice implements RequestBodyAdvice {
    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        if (inputMessage.getBody().available()<=0) {
            return inputMessage;
        }

        // 获取前端入参jsonobj
        byte[] requestBodyByte=new byte[inputMessage.getBody().available()];
        inputMessage.getBody().read(requestBodyByte);
        String requestData = new String(requestBodyByte, StandardCharsets.UTF_8);
        JSONObject jsonobj = JSON.parseObject(requestData);

        // 参数转换,将@TransParam修饰的字段,男->0,女->1,其他值->2;
        jsonobj = transParams(jsonobj, targetType);

        // 使用解密后的数据,构造新的读取流
        return getNewInputStream(jsonobj, inputMessage);
    }

    private JSONObject transParams(JSONObject jsonobj, Type targetType) {

        try {
            Class<?> clazz = Class.forName(targetType.getTypeName());
            // 获取接口参数类的所有属性
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                String fieldName = field.getName();
                // 如果属性被@TransParam修饰,则执行转换
                if (field.getAnnotation(TransParam.class) != null) {
                    String value = jsonobj.getString(fieldName);
                    if (value.equals("男")) {
                        jsonobj.put(fieldName, 0);
                    } else if (value.equals("女")) {
                        jsonobj.put(fieldName, 1);
                    } else {
                        jsonobj.put(fieldName, 2);
                    }
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return jsonobj;
    }

    private HttpInputMessage getNewInputStream(JSONObject jsonobj, HttpInputMessage inputMessage) {
        byte[] newRequestByte = jsonobj.toJSONString().getBytes();
        InputStream rawInputStream = new ByteArrayInputStream(newRequestByte);
        return new HttpInputMessage() {
            @Override
            public HttpHeaders getHeaders() {
                return inputMessage.getHeaders();
            }

            @Override
            public InputStream getBody() {
                return rawInputStream;
            }
        };
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }
}

自定义ResponseBodyAdvice返回值处理器

作用

        1、将@TransParam修饰的字段值,进行转换:0->男1,1->女1,其他值->中性;

        2、将返回值统一封装成ResponseResult对象返回给前端。

主要会用到supports和beforeBodyWrite两个方法:

supports判断是否对当前返回值进行值转换;

beforeBodyWrite执行具体的转换逻辑。

@ControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice {
    @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) {
        JSONObject jsonObject = null;
        try {
            Class<?> clazz = Class.forName(returnType.getParameterType().getName());
            // 获取返回值对象类的所有属性
            Field[] fields = clazz.getDeclaredFields();
            jsonObject = JSONObject.parseObject(JSONObject.toJSONString(body));
            for (Field field : fields) {
                String fieldName = field.getName();
                // 如果字段被@TransParam修饰,则进行数据转换:0->男1,1->女1,其他值->中性
                if (field.getAnnotation(TransParam.class) != null) {
                    String value = jsonObject.getString(fieldName);
                    if (value.equals("0")) {
                        jsonObject.put(fieldName, "男1");
                    } else if (value.equals("1")) {
                        jsonObject.put(fieldName, "女1");
                    } else {
                        jsonObject.put(fieldName, "中性");
                    }
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        // 将返回结果封装成ResponseResult对象统一返回。
        return ResponseResult.success(jsonObject);
    }
}

controller接口

@ResponseBody
    @PostMapping("/test")
    public User test(@RequestBody User user) {
        String result = "姓名:" + user.getName() + "; 性别编号:" + user.getSex();
        System.out.println(result);
        return user;
    }

测试

注意事项

自定义的处理对象类上必须得加上@ControllerAdvice注解;

RequestBodyAdvice作用范围为@RequestBody修饰的参数;

ResponseBodyAdvice作用范围为@ResponseBodyAdvice修饰的方法。

以上内容为个人学习理解,如有问题,欢迎在评论区指出。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐