Spring根据ResponseBodyAdvice<Object>和拦截器进行response统一格式返回设置

思路:自定义一个注解,拦截请求查看该请求处理的controller是否有该注解,有则包装response,否则直接返回。

  • 定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.Documented;

/**
 * 用来标记在controller层类or方法上
 * 表示请求该URI的response需要被转换
 *  * @author moguchen
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface ResponseConversion {
}
  • 定义通用response返回

/**
 * 通用返回体
 *
 * @author moguchen
 */
public class CommonResponse {

    /**
     * 返回码
     */
    private Integer code;

    /**
     * 是否请求处理成功
     */
    private Boolean success;

    /**
     * 返回信息描述
     */
    private String msg;

    /**
     * 返回数据
     */
    private Object data;

    public void setCode(Integer code) {
        this.code = code;
    }

    public void setSuccess(Boolean success) {
        this.success = success;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public Integer getCode() {
        return code;
    }

    public Boolean getSuccess() {
        return success;
    }

    public String getMsg() {
        return msg;
    }

    public Object getData() {
        return data;
    }

    /**
     * 成功的返回
     *
     * @param data data
     * @return response
     */
    public static CommonResponse success(Object data) {
        CommonResponse response = new CommonResponse();
        response.setCode(HttpStatus.OK.value());
        response.setMsg(HttpStatus.OK.getReasonPhrase());
        response.setSuccess(true);
        response.setData(data);
        return response;
    }

    /**
     * 失败的返回
     *
     * @param e error
     * @return response
     */
    public static CommonResponse failure(CommonException e) {
        CommonResponse response = new CommonResponse();
        response.setCode(e.getErrorCode());
        response.setMsg(e.getErrorMessage());
        response.setSuccess(false);
        return response;
    }
}

  • 请求拦截器处理,此处的常量为一个字符串标记
    /**
     * response需要被统一转换包装的标记
     */
    public static final String RESPONSE_NEED_CONVERSION_MARK = "RESPONSE_NEED_CONVERSION_MARK";
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

import static com.xxx.project.common.constant.ArmsConstant.RESPONSE_NEED_CONVERSION_MARK;

/**
 * request拦截器
 * 根据注解看此次请求的response是否需要被转换包装
 *
 * @author moguchen
 */
@Slf4j
@Component
public class ArmsRequestInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Class<?> clazz = handlerMethod.getBeanType();
            Method method = handlerMethod.getMethod();
            // 如果本次请求的controller的类上或者方法上带有注解 那么放置标记
            if (clazz.isAnnotationPresent(ResponseConversion.class) || method.isAnnotationPresent(ResponseConversion.class)) {
                request.setAttribute(RESPONSE_NEED_CONVERSION_MARK, true);
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }

}
  • 响应处理,这里采用Spring提供的ResponseBodyAdvice<Object>来处理,supports的返回表示此次请求是否要进行beforeBodyWrite方法的处理
import org.springframework.core.MethodParameter;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.servlet.http.HttpServletRequest;

import static com.xxx.project.common.constant.ArmsConstant.RESPONSE_NEED_CONVERSION_MARK;

/**
 * response转换类
 *  * @author moguchen
 */
@RestControllerAdvice
public class ArmsResponseAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter methodParameter,
                            Class<? extends HttpMessageConverter<?>> aClass) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return false;
        }
        // 看请求有没有带上mark标记 没有带response不需要转换包装
        HttpServletRequest request = requestAttributes.getRequest();
        return !ObjectUtils.isEmpty(request.getAttribute(RESPONSE_NEED_CONVERSION_MARK));
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType,
                                  Class<? extends HttpMessageConverter<?>> aClass,
                                  ServerHttpRequest serverHttpRequest,
                                  ServerHttpResponse serverHttpResponse) {
        // 本身就为转换的response或者文件上传下载 不需要转换 直接返回
        if (o instanceof CommonResponse || o instanceof Resource) {
            return o;
        }
        return CommonResponse.success(o);
    }
}
  • 完成之后只需要在controller层加上该注解即可,然后只需要正常返回需要包装的对象即可,可以省去冗余代码。
/**
 * 测试用controller
 *  * @author moguchen
 */
@RestController
@ResponseConversion
@RequestMapping("/demo")
public class DemoController {

    private final DemoService demoService;

    public DemoController(DemoService demoService) {
        this.demoService = demoService;
    }

    @GetMapping("/{name}")
    public DemoResponse getDemo(@PathVariable String name) {
        return demoService.getDemo(name);
    }
}
  • 异常的处理,异常不会走到ResponseBodyAdvice<Object>的方法中来,而会被直接抛出,所以我们需要自定义全局的异常处理,这里贴出几个常用的异常

/**
 * 全局异常处理
 *
 * @author moguchen
 */
@Slf4j
@ResponseBody
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = CommonException.class)
    @ResponseStatus(code = HttpStatus.OK)
    public CommonResponse baseExceptionHandler(CommonException e) {
        log.error("自定义异常捕获:" + e.getErrorMessage(), e);
        return CommonResponse.failure(e);
    }

    @ExceptionHandler(value = {MethodArgumentNotValidException.class, BindException.class})
    @ResponseStatus(code = HttpStatus.OK)
    public CommonResponse baseExceptionHandler(Exception e) {
        BindingResult result;
        if (e instanceof MethodArgumentNotValidException) {
            result = ((MethodArgumentNotValidException) e).getBindingResult();
        } else {
            result = ((BindException) e).getBindingResult();
        }
        List<String> res = new ArrayList<>();
        if (result.hasErrors()) {
            List<FieldError> fieldErrors = result.getFieldErrors();
            for (FieldError error : fieldErrors) {
                res.add(error.getDefaultMessage());
            }
        } else {
            res = Collections.emptyList();
        }
        String errorMsg = String.join(",", res);
        log.error("参数异常捕获:" + errorMsg, e);
        return CommonResponse.failure(new CommonException(HttpStatus.BAD_REQUEST.value(), errorMsg));
    }

    @ExceptionHandler(value = NullPointerException.class)
    @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
    public CommonResponse exceptionHandler(NullPointerException e) {
        log.error("空指针异常捕获:", e);
        return CommonResponse.failure(new CommonException(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()));
    }

    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
    public CommonResponse exceptionHandler(Exception e) {
        log.error("未知异常捕获:", e);
        return CommonResponse.failure(new CommonException(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()));
    }

}

然后由于我们在beforeBodyWrite方法中判断了是CommonResponse就会直接返回,所以这里response不会被重复包装。

@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType,
                              Class<? extends HttpMessageConverter<?>> aClass,
                              ServerHttpRequest serverHttpRequest,
                              ServerHttpResponse serverHttpResponse) {
    // 本身就为转换的response或者文件上传下载 不需要转换 直接返回
    if (o instanceof CommonResponse || o instanceof Resource) {
        return o;
    }
    return CommonResponse.success(o);
}
  • service实现类test处理
/**
 * @author moguchen
 */
@Service
public class DemoServiceImpl implements DemoService {

    @Override
    public DemoResponse getDemo(String name) {
        if ("alice".equals(name)) {
            throw new CommonException(UserExceptionEnum.USER_NOT_EXIST_ERROR);
        }
        DemoResponse response = new DemoResponse();
        response.setName(name);
        response.setAddress("address");
        response.setSchool("school");
        return response;
    }

}
  • 测试
    正常的response

    异常抛出的response
    在这里插入图片描述
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,我无法找到与问题中的<Response> <statusCode>160001</statusCode> <statusMsg>请求方法不支持</statusMsg> </Response> 相关的信息。请提供更多上下文或详细信息,以便我能够更好地回答您的问题。 #### 引用[.reference_title] - *1* [Spring根据ResponseBodyAdviceObject拦截器进行response统一格式返回设置](https://blog.csdn.net/weixin_50385593/article/details/117634160)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [webapi <Message>已拒绝为此请求授权。</Message>](https://blog.csdn.net/aihou8107/article/details/101639123)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [python Response [200]返回值问题 + 解析requests返回response(json格式)说明](https://blog.csdn.net/Programmer_FuQiang/article/details/120438609)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值