spring mvc 统一返回格式

自己写个方法,实现ResponseBodyAdvice接口,如

import cn.linkengine.pre.service.config.mvc.annotation.ResponseBabyMessage;
import cn.linkengine.pre.service.config.mvc.annotation.SimpleResponseBody;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * @author: yuhh
 * @create: 2019-01-07 15:17
 **/
@ControllerAdvice
public class SimpleResponseBodyAdvice implements ResponseBodyAdvice {

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return !returnType.getMethod().getReturnType().isAssignableFrom(JsonVo.class) && (returnType.hasMethodAnnotation(SimpleResponseBody.class) || AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), SimpleResponseBody.class));
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return JsonVo.buildSuccess(body);
    }
}

@ControllerAdvice:可以对控制器的进行全局配置

supports(MethodParameter returnType, Class converterType):判断是否需要进行处理,返回true则会进行拦截并统一返回格式

//如果本次请求的返回值是否是JsonVo,不是的话才需要拦截
!returnType.getMethod().getReturnType().isAssignableFrom(JsonVo.class) 
//本次请求的方法上是否有SimpleResponseBody注解,有的话才需要拦截
&& (returnType.hasMethodAnnotation(SimpleResponseBody.class) ||
// 本次请求的方法上是否有注解包含SimpleResponseBody注解,有的话才需要拦截
AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), SimpleResponseBody.class));
beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response):对返回值进行统一处理

以下是我用到的注解:

import org.springframework.web.bind.annotation.ResponseBody;

import java.lang.annotation.*;

/**
 * @Auther: yuhh
 * @Date: 2019/1/7 15:14
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ResponseBody
public @interface SimpleResponseBody {
}
import org.springframework.stereotype.Controller;

import java.lang.annotation.*;

/**
 * @Auther: yuhh
 * @Date: 2019/1/7 15:12
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@SimpleResponseBody
public @interface SimpleRestController {
}

然后在controller上标注SimpleRestController 注解,如

import cn.linkengine.pre.service.config.mvc.annotation.SimpleRestController;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @Auther: yuhh
 * @Date: 2018/7/6 17:01
 */
@SimpleRestController
@RequestMapping("/dictionaryController")
@Api(description = "数据字典api")
public class DictionaryController {

   
}

注意:SimpleRestController注解中因为标注了SimpleResponseBody注解,而SimpleResponseBody注解中又标注了ResponseBody注解,因此SimpleRestController注解是和RestController注解一样,无法返回jsp页面,或者html页面

这样会有一个问题,那就是无法controller中的方法无法返回String类型的返回值,不然会报类型转换失败的异常,我想了个旁门左道去解决该问题

新建一个注解

import java.lang.annotation.*;

/**
 * 设置需要返回的String值
 * @Auther: yuhh
 * @Date: 2019/1/9 15:01
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBabyMessage {

    String message() default "";
}

然后修改ResponseBodyAdvice 的beforeBodyWrite方法

@Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        // 判断该方法上是否有ResponseBabyMessage注解
        if (returnType.hasMethodAnnotation(ResponseBabyMessage.class)){
            String message = returnType.getMethodAnnotation(ResponseBabyMessage.class).message();
            }
            return JsonVo.buildSuccessMessage(message);
        }
        return JsonVo.buildSuccess(body);
    }

但这样又会出现一个问题,那就是这个系统中该方法都只能有一个返回值,无法在方法里动态的修改返回值,我又想了个旁门左道去解决该问题

import cn.linkengine.pre.service.config.mvc.annotation.ResponseBabyMessage;

import java.lang.reflect.Method;

/**
 * @author: yuhh
 * @create: 2019-01-09 15:23
 **/
public class ChangeResponseBabyMessage {

    private static ThreadLocal<String> messageThreadLocal = new ThreadLocal<>();

    /**
     * 如果满足该类中有指定方法且方法上有ResponseBabyMessage注解,则改变其方法上的ResponseBabyMessage的message值
     * 因为注解是单例,所以用了迂回的方式,声明一个线程变量
     * @param: cls:指定类
     * @param: methodName:指定方法名
     * @param: message:需要改变的message值
     * @return: void
     * @author: yuhh
     * @date: 2019/1/10 11:10
     */
    public static void changeMeaage(Class cls, String methodName, String message){

        //如果满足该类中有指定方法且方法上有ResponseBabyMessage注解,则改变其方法上的ResponseBabyMessage的message值
        Method[] methods = cls.getDeclaredMethods();
        for (Method m : methods){
            if (m.getName().equals(methodName) && m.isAnnotationPresent(ResponseBabyMessage.class)){
                messageThreadLocal.set(message);
                break;
            }
        }

        // 因为注解是单例,所以不满足需求,故废弃之
//        Method[] methods = cls.getDeclaredMethods();
//        ResponseBabyMessage responseBabyMessage = null;
//        for (Method m : methods){
//            if (m.getName().equals(methodName) && m.isAnnotationPresent(ResponseBabyMessage.class)){
//                responseBabyMessage = m.getDeclaredAnnotation(ResponseBabyMessage.class);
//                break;
//            }
//        }
//        try {
//            //获取 responseBabyMessage 这个代理实例所持有的 InvocationHandler
//            InvocationHandler h = Proxy.getInvocationHandler(responseBabyMessage);
//            // 获取 AnnotationInvocationHandler 的 memberValues 字段
//            Field hField = h.getClass().getDeclaredField("memberValues");
//            // 因为这个字段事 private final 修饰,所以要打开权限
//            hField.setAccessible(true);
//            // 获取 memberValues
//            LinkedHashMap memberValues = (LinkedHashMap) hField.get(h);
//            // 修改 message 属性值
//            memberValues.put("message", message);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
    }

    /**
     * 获取线程变量message的值
     * @param:
     * @return: java.lang.String
     * @author: yuhh
     * @date: 2019/1/10 13:58
     */
    public static String getMessage(){
        return messageThreadLocal.get();
    }

    /**
     * 清除线程变量message的值
     * @param:
     * @return: void
     * @author: yuhh
     * @date: 2019/1/10 13:59
     */
    public static void cleanMessage(){
        messageThreadLocal.remove();
    }
}

同时修改ResponseBodyAdvice 的beforeBodyWrite方法

@Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        // 判断该方法上是否有ResponseBabyMessage注解
        if (returnType.hasMethodAnnotation(ResponseBabyMessage.class)){
            // 先取线程变量的值
            String message = ChangeResponseBabyMessage.getMessage();
            // 如果线程变量的值不为空,则说明message已经被修改了
            if (!StringUtils.isEmpty(message)){
                // 取完值后移除线程变量的值
                ChangeResponseBabyMessage.cleanMessage();
            } else {
                // 如果线程变量的值为空,则去注解上的message默认值
                message = returnType.getMethodAnnotation(ResponseBabyMessage.class).message();
            }
            return JsonVo.buildSuccessMessage(message);
        }
        return JsonVo.buildSuccess(body);
    }

放个例子上来

import cn.linkengine.pre.service.business.camera.domain.entity.PsCameraParkingLotConfig;
import cn.linkengine.pre.service.business.camera.park.service.ParkingLotConfigService;
import cn.linkengine.pre.service.config.mvc.ChangeResponseBabyMessage;
import cn.linkengine.pre.service.config.mvc.annotation.ResponseBabyMessage;
import cn.linkengine.pre.service.config.mvc.annotation.SimpleRestController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author: yuhh
 * @create: 2018-10-16 14:18
 **/
@SimpleRestController
@RequestMapping("parkingLotConfigController")
@Api(description = "停车场接口配置api")
public class ParkingLotConfigController {

    @Autowired
    private ParkingLotConfigService parkingLotConfigService;

    @PostMapping(value = "saveParkInterface")
    @ApiOperation(value = "保存停车场接口", notes = "保存停车场接口")
    @ResponseBabyMessage(message = "保存成功")
    public void saveParkInterface(PsCameraParkingLotConfig psCameraParkingLotConfig, String userId){
        psCameraParkingLotConfig.setUpdateUserId(userId);
        if (parkingLotConfigService.canSaveByParkingLotIdAndInterfaceCode(psCameraParkingLotConfig)){
            if (psCameraParkingLotConfig.getId() == null){
                psCameraParkingLotConfig.setCreateUserId(userId);
                parkingLotConfigService.singSave(psCameraParkingLotConfig);
            } else {
                parkingLotConfigService.update(psCameraParkingLotConfig);
            }
        } else {
            ChangeResponseBabyMessage.changeMeaage(this.getClass(), "saveParkInterface", "该停车场已经配置了该接口,无需再次配置");
        }
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值