springboot 根据url动态控制序列化字段

分类: spring-boot
阅读:604
作者:majingjing
发布:2020-07-01 11:31:11

springboot的出现简化了web程序的开发, 想返回一个json的内容,只需在controller上加上 @RestController 即可实现. 非常方便.

但是项目中往往会有一些特殊的场景, 需要我们对返回json内容做特殊定制化处理.

需求场景

比如一个返回json内容

{
    "name":"孙悟空",
    "img":"http://www.majingjing.cn/static/123.png",
    "bankCard":{
        "bankName":"农业银行",
        "cardId":"ABC-10001"
    }
}

现在有两个url请求, 分别会得到这个user的json内容

  • GET /customer/user
  • GET /admin/user

此时有个场景是这样的

  • GET /customer/user 返回内容如下
{
    "name":"孙悟空",
    "img":"http://www.majingjing.cn/static/123.png"
}
  • GET /admin/user
{
    "name":"孙悟空",
    "img":"http://www.majingjing.cn/static/123.png",
    "bankCard":{
        "bankName":"农业银行",
        "cardId":"ABC-10001"
    }
}

返回内容为全部信息

分析

在不考虑写多个UserDto的情况下, 如何来实现了. 我们看下如何利用 jackson 来完成不同url来动态处理 对象的序列化操作.


实现

定义UserDto
@Getter
@Setter
public class UserDto {

    private String name;
    private String img;
    private BankCard bankCard;

    @Getter
    @Setter
    public static class BankCard{
        private String bankName;
        private String cardId;
    }

}
定义Api
@RestController
public class UserController {

    @GetMapping("/customer/user")
    public UserDto user(){
        return getUser();
    }

    @GetMapping("/admin/user")
    public UserDto adminUser(){
        return getUser();
    }

    private UserDto getUser(){
        UserDto.BankCard bankCard = new UserDto.BankCard();
        bankCard.setBankName("农业银行");
        bankCard.setCardId("ABC-10001");

        UserDto userDto = new UserDto();
        userDto.setName("孙悟空");
        userDto.setImg("http://www.majingjing.cn/static/123.png");
        userDto.setBankCard(bankCard);

        return userDto;
    }
}
自定义MappingJackson2HttpMessageConverter
public abstract class AbstractMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {

    public AbstractMappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        super(objectMapper);
    }

    /**
     * 判断给定的url是否匹配
     *
     * @param url 请求的url
     * @return boolean 是否匹配
     */
    abstract boolean isMatch(String url);

    private boolean isMatch() {
        //从上下文中拿到请求地址
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if(null ==requestAttributes){
            return false;
        }
        String requestPath = requestAttributes.getRequest().getRequestURI();
        if (StringUtils.isBlank(requestPath)) {
            return false;
        }
        return isMatch(requestPath);
    }

    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        if (!isMatch()) {
            return false;
        }
        return super.canRead(clazz, mediaType);
    }

    @Override
    public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
        if (!isMatch()) {
            return false;
        }
        return super.canRead(type, contextClass, mediaType);
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        if (!isMatch()) {
            return false;
        }
        return super.canWrite(clazz, mediaType);
    }

    @Override
    public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
        if (!isMatch()) {
            return false;
        }
        return super.canWrite(type, clazz, mediaType);
    }

}
public class AdminMappingJackson2HttpMessageConverter extends AbstractMappingJackson2HttpMessageConverter {

    public AdminMappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        super(objectMapper);
    }

    /**
     * 判断给定的url是否匹配
     *
     * @param url 请求的url
     * @return boolean 是否匹配
     */
    boolean isMatch(String url){
        return url.startsWith("/admin/");
    }

}
``````java
public class CustomerMappingJackson2HttpMessageConverter extends AbstractMappingJackson2HttpMessageConverter {

    public CustomerMappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        super(objectMapper);
    }

    /**
     * 判断给定的url是否匹配
     *
     * @param url 请求的url
     * @return boolean 是否匹配
     */
    boolean isMatch(String url){
        return url.startsWith("/customer/");
    }

}
配置HttpMessageConverter
@JsonIgnoreType
public @interface IgnoreType {
}
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        Jackson2ObjectMapperBuilder customerBuilder = new Jackson2ObjectMapperBuilder()
                //忽略UserDto.BankCard
                .mixIn(UserDto.BankCard.class, IgnoreType.class);

        Jackson2ObjectMapperBuilder adminBuilder = new Jackson2ObjectMapperBuilder();
        converters.add(0, new CustomerMappingJackson2HttpMessageConverter(customerBuilder.build()));
        converters.add(1, new AdminMappingJackson2HttpMessageConverter(adminBuilder.build()));

    }
}

此时我们已经将自定义的两个 HttpMessageConverter 加人到WebConfiguration中了

结果验证

启动项目, 验证下结果

可以看到已经实现了预期的结果, 实现了

  • GET /customer/user 不显示 bankCard
  • GET /admin/user 显示 bankCard

通过此示例介绍, 我们可以在项目中配合jackson完成各种定制化的操作.

比如之前写过的文章, 多语言 , 特殊对象的解析等.