你知道有哪些Spring MVC扩展点可以解析接口参数和处理返回值吗?

1.概述

Spring MVC 是一个灵活且强大的框架,它允许开发者在框架的基础上进行深度定制,以满足各种复杂的业务需求。HandlerMethodArgumentResolverHandlerMethodReturnValueHandlerSpring MVC 提供的两个重要扩展点,分别用于处理控制器方法的参数解析和返回值处理。本文将详细探讨这两个接口的作用、使用场景以及如何自定义实现。

关于Spring MVC的扩展点,我们之前已经总结过的两个扩展点:

谈谈@ControllerAdvice的使用及其实现原理:用于定义全局的异常处理、数据绑定、数据预处理等功能。

一文带你掌握SpringMVC扩展点RequestBodyAdvice和ResponseBodyAdvice如何使用及实现原理RequestBodyAdvice允许开发者在处理 HTTP 请求体之前或之后插入自定义逻辑。它通过与 HttpMessageConverter 紧密集成,在请求体读取和转换的过程中提供了扩展点。了解其工作原理有助于在复杂的请求处理场景中实现更强大的功能,如日志记录、数据预处理加解密和签名验证等。ResponseBodyAdvice 是一个强大的工具,允许在 Spring MVC 中对响应数据进行集中处理和修改。通过自定义 ResponseBodyAdvice 实现类,可以实现响应数据的加密、格式转换、统一包装等多种功能,提升代码的可维护性和一致性。其实HandlerMethodArgumentResolverHandlerMethodReturnValueHandler实现的场景功能也是差不多的,都是对接口的参数解析和返回结果加工处理,但是今天这里就不再也对接口入参和返回结果加解密操作进行示例,感兴趣可以根据前面总结的文章提到的加解密需求功能用本文讲解的扩展点自行实现一波,那就真的学到了~~~

2.HandlerMethodArgumentResolver

HandlerMethodArgumentResolver 接口的主要作用是将 HTTP 请求中的参数解析为控制器方法的参数。Spring MVC 默认提供了多种 HandlerMethodArgumentResolver 的实现,如解析 @RequestParam@PathVariable@RequestBody 等注解的参数。

当一个请求到达时,Spring MVC 会遍历已注册的 HandlerMethodArgumentResolver,找到能够支持该方法参数的解析器,并调用其 resolveArgument 方法进行参数解析。定义如下:

public interface HandlerMethodArgumentResolver {
   

    /**
     * 是否支持解析该参数
     */
    boolean supportsParameter(MethodParameter parameter);

    /**
     * 解析该参数
     *
     */
    @Nullable
    Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;

}

从源码来看这个接口定义很简单,#supportsParameter()是否支持解析该参数,如果支持就调用#resolveArgument(),话不多说直接来看实际开发中的应用场景,我们都知道一般在业务系统开发中,客户端访问服务端接口一般都需要走登录认证,把当前用户信息放到请求的上下文,以便后续获取当前登陆用户信息做逻辑处理

RequestUserHolder.getCurrentUser()

但是有时候想把登录信息当做方法参数进行传递,如下所示:

    @GetMapping("/user")
    public User getUserInfo(@RequestParam("userId") Long userId) {
   
        return userService.getUserInfo(userId);
    }

这是错误的示范,个人标识通过客户端传参,这意味着客户端想传谁的userId都行,这就导致了严重的数据安全问题。此时你可能在想有没有办法把userId作为方法参数,但是不再通过客户端传参,而是登录认证之后在请求上下文中获取。完全可以,通过HandlerMethodArgumentResolver就可以实现。

首先可以自定义一个注解标识@LoginUseruserInfo参数上,其作用不言而喻就是为了上面源码定义提到的方法#supportsParameter()满足解析参数条件。

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoginUser {
   
}

接下来就是实现HandlerMethodArgumentResolver完成参数解析,也就是通过在请求上下文中获取登陆信息对参数userId进行赋值操作:

@Slf4j
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
   


    /**
     * 入参筛选
     *
     * @param methodParameter 参数集合
     * @return 格式化后的参数
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
   
        return methodParameter.hasParameterAnnotation(LoginUser.class) && methodParameter.getParameterType().equals(UserSession.class);
    }

    /**
     * @param methodParameter       入参集合
     * @param modelAndViewContainer model 和 view
     * @param nativeWebRequest      web相关
     * @param webDataBinderFactory  入参解析
     * @return 包装对象
     */
    @Override
    public Object resolveArgument(MethodParameter methodParameter,
                                  ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest,
                                  WebDataBinderFactory webDataBinderFactory) {
   
        return getCurrentUser(nativeWebRequest);
    }

    private UserSession getCurrentUser(NativeWebRequest webRequest) {
   
        // 这里是获取当前用户的逻辑
        // 1.你可以从请求信息中获取
        // HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        // ...

        // 2.也可以从登陆认证之后的上下文中获取
        // UserSession currentUser = RequestUserHolder.getCurrentUser();

        // 这里为了示例,就直接返回一个userSession进行模拟了
        UserSession session = new UserSession();
        session.setId(8L);
        session.setName("张三");
        session.setOrgId(6L);
        return session;
    }


}

当然,最后我们要实现 WebMvcConfigurer 接口的 #addArgumentResolvers() 方法,来增加这个自定义的处理器 LoginUserArgumentResolver

@Configuration
public class DefaultWebMvcConfig implements WebMvcConfigurer {
   


    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
   
        resolvers.add(new LoginUserArgumentResolver());
    }
}

通过上面参数解析配置之后,就可以通过参数解析赋值接口方法参数了。

   @GetMapping("/loginUser")
    public User getUser(@RequestParam("id") Long id, @LoginUser UserSession session) {
   
        Long userId = session.getId();
        User user = userService.getUser(userId);
        return user;
    }

postman调用接口:

从结果可以看出通过LoginUserArgumentResolver参数解析器获得userSession的userId为8,去查询数据库返回。

接口方法中我们虽然使用@RequestParam("id"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值