处理Spring中使用JsonView与自定义返回结果切面的冲突

关于JSONVIEW

在工程中使用JSONVIEW来自定义返回的JSON字段,通过在实体类中配置不同的view来设置不同的返回类型,然后中通过@JsonView注解来选择相应的返回view,例如:

  • 在实体类中定义不同的view
public class User implements Serializable {

    private static final long serialVersionUID = 2158782964550177013L;

    public class BaseView{}

    public class PublicView extends BaseView{}

    public class FullView extends PublicView{}

    @JsonView(User.BaseView.class)
    private String id = "1";

    @JsonView(User.BaseView.class)
    private String username = "sunqian";

    @JsonView(User.PublicView.class)
    private String name = "sunqian";

    @JsonView(User.FullView.class)
    private String password = "12344321";

    @JsonView(User.FullView.class)
    private String auth = "administrator";

    @JsonView(User.FullView.class)
    private String personalId = "320121************";

    @JsonView(User.PublicView.class)
    private String location = "nanjing";

    @JsonView(User.PublicView.class)
    private String mobile = "15959595959";

    @JsonView(User.FullView.class)
    private String degree = "bd";
    
    // 为了方便,直接定义一些默认值,get/set省略
}
  • 在controller中定义三种访问接口
@RestController
@CrossOrigin
@RequestMapping("jsonview/")
public class JsonViewTestController {

    @RequestMapping(value = "getBaseUserInfo", method = {RequestMethod.GET})
    @JsonView(User.BaseView.class)
    public Object getBaseUserInfo(){
        return new User();
    }

    @JsonView(User.PublicView.class)
    @RequestMapping(value = "getPublicUserInfo", method = {RequestMethod.GET})
    public Object getPublicUserInfo(){
        return new User();
    }

    @JsonView(User.FullView.class)
    @RequestMapping(value = "getFullUserInfo", method = {RequestMethod.GET})
    public Object getFullUserInfo(){
        return new User();
    }

}
  • 分别访问3个接口,可以分别返回在实体类中定义的3种view,分别是:
getBaseUserInfo
{
    "id": "1",
    "username": "sunqian"
}
getPublicUserInfo
{
    "id": "1",
    "username": "sunqian",
    "name": "sunqian",
    "location": "nanjing",
    "mobile": "15959595959"
}
getFullUserInfo
{
    "id": "1",
    "username": "sunqian",
    "name": "sunqian",
    "password": "12344321",
    "auth": "administrator",
    "personalId": "320121************",
    "location": "nanjing",
    "mobile": "15959595959",
    "degree": "bd"
}

与自定义返回结果的冲突

自定义一个转换返回结果的切面

有时候我们会自定义一些返回结果的格式,比如用于统一返回格式或者是规范对一些异常的捕获,比如在上述的测试代码中加入简单的异常捕获。

  • 添加自定义返回格式的实体类
public class Result implements Serializable {

    private static final long serialVersionUID = -953751321435659670L;
    private String status;

    private Object body;

    public Result() {
    }

    public Result(String status, Object body) {
        this.status = status;
        this.body = body;
    }

    // get/set方法省略
}
  • 添加注解,通过使用注解作为切点来织入
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResultMap {
    
}
  • 完成切面,并在controller中加入@ResultMap注解
@Aspect
@Component
public class ResultMapping {

    @Around("@annotation(com.sunqian.test.entity.ResultMap)")
    public Object resultMapping(ProceedingJoinPoint joinPoint){
        try {
            return new Result("200", joinPoint.proceed());
        } catch (Exception ex) {
            return new Result("500", ex.getMessage());
        } catch (Throwable throwable) {
            return new Result("500", throwable.toString());
        }
    }

}
@RestController
@CrossOrigin
@RequestMapping("jsonview/")
public class JsonViewTestController {

    @RequestMapping(value = "getFullUserInfo", method = {RequestMethod.GET})
    @ResultMap
    public Object getFullUserInfo(){
        return new User();
    }

}

这时请求上述的接口可以返回以下格式的结果

{
    "status": "200",
    "body": {
        "id": "1",
        "username": "sunqian",
        "name": "sunqian",
        "password": "12344321",
        "auth": "administrator",
        "personalId": "320121************",
        "location": "nanjing",
        "mobile": "15959595959",
        "degree": "bd"
    }
}

与JsonView发生冲突

在controller中同时加入@JsonView注解

@RestController
@CrossOrigin
@RequestMapping("jsonview/")
public class JsonViewTestController {

    @RequestMapping(value = "getBaseUserInfo", method = {RequestMethod.GET})
    @JsonView(User.BaseView.class)
    @ResultMap
    public Object getBaseUserInfo(){
        return new User();
    }

    @JsonView(User.PublicView.class)
    @ResultMap
    @RequestMapping(value = "getPublicUserInfo", method = {RequestMethod.GET})
    public Object getPublicUserInfo(){
        return new User();
    }

    @JsonView(User.FullView.class)
    @RequestMapping(value = "getFullUserInfo", method = {RequestMethod.GET})
    @ResultMap
    public Object getFullUserInfo(){
        return new User();
    }

}

这时再请求这3个接口可以看到接口返回了空的对象,很显然这个是错误,经分析可以知道,应该是自定义的切面在处理返回值的时候,与JsonView发生了冲突。

解决冲突

原本想的是JsonView应该也是通过切面的方式来处理结果集的,是不是可以通过设置切面的请求顺序来解决呢?最后尝试了不同的方法,返回的结果都是空的,只能转而通过其他方法来处理了。

通过查看文档了解到,除了使用注解的方式来使用JsonView,还可以通过在自定义的切面的手动调用相关方法来使用。

  • 修改切面的代码,添加使用JSONVIEW过滤实体类属性的功能
@Aspect
@Component
public class ResultMapping {

    @Autowired
    private Gson gson;

    @Around("@annotation(com.sunqian.test.entity.ResultMap)")
    public Object resultMapping(ProceedingJoinPoint joinPoint){
        try {
            MethodInvocationProceedingJoinPoint mjp = (MethodInvocationProceedingJoinPoint) joinPoint;
            MethodSignature signature = (MethodSignature) mjp.getSignature();
            Method method = signature.getMethod();
            ResultMap resultMap = method.getAnnotation(ResultMap.class);
            ObjectMapper mapper = new ObjectMapper();
            return new Result("200", gson.fromJson(mapper.writerWithView(resultMap.view()).writeValueAsString(joinPoint.proceed()), Map.class));
        } catch (Exception ex) {
            return new Result("500", ex.getMessage());
        } catch (Throwable throwable) {
            return new Result("500", throwable.toString());
        }
    }

}
  • @ResultMap注解类中定义view属性
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResultMap {
    Class view();
}
  • 将jsonview整合到自定义的注解中
@RestController
@CrossOrigin
@RequestMapping("jsonview/")
public class JsonViewTestController {

    @RequestMapping(value = "getBaseUserInfo", method = {RequestMethod.GET})
    @ResultMap(view = User.BaseView.class)
    public Object getBaseUserInfo(){
        return new User();
    }

    @ResultMap(view = User.PublicView.class)
    @RequestMapping(value = "getPublicUserInfo", method = {RequestMethod.GET})
    public Object getPublicUserInfo(){
        return new User();
    }

    @RequestMapping(value = "getFullUserInfo", method = {RequestMethod.GET})
    @ResultMap(view = User.FullView.class)
    public Object getFullUserInfo(){
        return new User();
    }

}

这时,再请求这3个接口,可以得到:

getBaseUserInfo
{
    "status": "200",
    "body": {
        "id": "1",
        "username": "sunqian"
    }
}
getPublicUserInfo
{
    "status": "200",
    "body": {
        "id": "1",
        "username": "sunqian",
        "name": "sunqian",
        "location": "nanjing",
        "mobile": "15959595959"
    }
}
getFullUserInfo
{
    "status": "200",
    "body": {
        "id": "1",
        "username": "sunqian",
        "name": "sunqian",
        "password": "12344321",
        "auth": "administrator",
        "personalId": "320121************",
        "location": "nanjing",
        "mobile": "15959595959",
        "degree": "bd"
    }
}

最后

在上述的示意代码中我只在自定义注解中加入了接收JsonView的view参数,事实上在实际的开发中可能还需要加入其他的参数,比如参数返回值的类型,包括对object,array等不同类型的处理,示意代码中只对object类型作了处理

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值