使用Spring 数据绑定机制,让RESTful API代码更简洁

默认情况下,Spring 只知道如何转换简单数据类型。比如我们提交的 intStringboolean 类型的请求数据,它会自动绑定到与之对应的 Java 类型。但在实际项目中,远远不够,因为我们可能需要绑定更复杂的对象类型。
我们需要了解 Spring 数据绑定机制,这样我们就可以更灵活的做全局配置或自定义配置,进而让我们的 RESTful API 更简洁,可读性也更好。

Spring 数据绑定

日期绑定

先来看下面一小段代码

@Controller
@RequestMapping("/index")
@Slf4j
public class IndexController {
  @GetMapping("/{date}")
  public void getSpecificDateInfo(@PathVariable LocalDateTime date) {
    log.info(date.toString());
  }

}

当我们用 Postman 请求这个 API

localhost:8080/index/2019-12-10 12:00:00

如我们所料,抛出数据类型转换异常

在这里插入图片描述
因为 Spring 默认不支持将 String 类型的请求参数转换为 LocalDateTime 类型,所以我们需要自定义 converter 「转换器 完整整个转换过程

自定义转换器 StringToLocalDateTimeConverter,使其实现 org.springframework.core.convert.converter.Converter<S, T> 接口,在重写的 convert 方法中实现我们自定义的转换逻辑

public class StringToLocalDateTimeConverter implements Converter<String, LocalDateTime> {
  @Override
  public LocalDateTime convert(String s) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.CHINESE);
    return LocalDateTime.parse(s, formatter);
  }
}

将转换器注册到上下文中:

@Configuration
public class UnifiedReturnConfig implements WebMvcConfigurer {
  @Override
  public void addFormatters(FormatterRegistry registry) {
    registry.addConverter(new StringToLocalDateTimeConverter());
  }
}

重新访问上面链接,查看控制台,按照预期得到相应转换结果:

2019-12-16 17:01:56.848  INFO 6420 --- [nio-8081-exec-3] com.lx.controller.IndexController        : 2019-12-10T12:00

知道了这个,比如我们常用的枚举类型也可以应用这种方式做数据绑定

枚举类型绑定

同样的套路,自定义转换器,Modes为自己创建的枚举类

public class StringToEnumConverter implements Converter<String, Modes> {
  @Override
  public Modes convert(String s) {
    return Modes.valueOf(s);
  }
}

将其添加至上下文,请小伙伴们自行尝试吧,知道了这个,我们再也不用在 RESTful API 内部做数据转换了,我们做到了全局控制,同时让整个 API 看起来更加清晰简洁

绑定对象

在某些情况下,我们希望将数据绑定到对象,这时我们可能马上联想起来使用 @RequestBody 注解,该注解通常用于获取 POST 请求体,并将其转换相应的数据对象
在实际业务场景中,除了请求体中的数据,我们同样需要请求头中的数据,比如tokentoken 中包含当前登陆用户的信息,每一次 RESTful 请求我们都需要从 header 中获取 token 数据处理实际业务,这种场景,上文提到的 Converter 以及 @RequestBody 显然不能满足我们的需求,此时我们就要换另一种解决方案 : HandlerMethodArgumentResolver
首先我们需要自定义一个注解 LoginUser (运行时生效,作用于参数上)

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

然后自定义 LoginUserArgumentResolver ,使其实现 HandlerMethodArgumentResolver 接口

public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
  @Override
  public boolean supportsParameter(MethodParameter methodParameter) {
    // 判断参数是否有自定义注解 LoginUser修饰
    return methodParameter.hasParameterAnnotation(LoginUser.class);
  }

  @Override
  public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
    HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest();
    LoginUserVo loginUserVo = new LoginUserVo();
    String token = request.getHeader("token");
    if (StringUtils.isNotBlank(token)) {
      //通常这里需要编写 token 解析逻辑,并将其放到 LoginUserVo 对象中,此处省略
      //logic
    }

    //在此为了快速简洁的做演示说明,省略掉解析 token 部分,直接从 header 指定 key 中获取数据
    loginUserVo.setId(Long.valueOf(request.getHeader("userId")));
    loginUserVo.setName(request.getHeader("userName"));
    return loginUserVo;
  }
}

依旧将自定义的 LoginUserArgumentResolver 添加到上下文中

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

编写 API:

  @GetMapping("/id")
  public void getLoginUserInfo(@LoginUser LoginUserVo loginUserVo) {
    log.info(loginUserVo.toString());
  }

通过 Postman 请求,在 header 中设置好相应的 K-V,如下图

localhost:8081/index/id

在这里插入图片描述
发送请求,查看控制台,得到预期结果

c.e.unifiedreturn.api.BindingController  : LoginUserVo(id=111111, name=rgyb)

相信到这里,你已经了解了基本的使用

作者:日拱一兵
链接:https://juejin.im/post/5df6d2eaf265da33bc57db96
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值