自定义注解实现返回API统一格式数据

通过添加在WEB层的自定义注解来判断是否对接口返回格式数据进行处理

1.新建注解

package com.api.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ResponseResult {

}

2.新建拦截器,并配置拦截器

拦截器用来拦截请求,并根据该URI是否添加@ResponseResult来设置该RUI是否需要对返回数据进行封装

package com.api.interceptor;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.api.annotation.ResponseResult;
import com.api.cache.ResponseURICache;

@Component
public class ResponsResultInterceptor 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();
            String rui = request.getRequestURI();
            if(null == ResponseURICache.getInstance().get(rui)) {
                if(clazz.isAnnotationPresent(ResponseResult.class)){
                    ResponseURICache.getInstance().set(rui, true);
                }else if(method.isAnnotationPresent(ResponseResult.class)){
                    ResponseURICache.getInstance().set(rui, true);
                }else{
                    ResponseURICache.getInstance().set(rui, false);
                }
            }
        }
        return true;
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception)
            throws Exception {
        // TODO Auto-generated method stub

    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView view)
            throws Exception {
        // TODO Auto-generated method stub

    }

}

配置拦截器

package com.api.config;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.api.interceptor.ResponsResultInterceptor;

@Configuration
public class WebConfigurer implements WebMvcConfigurer {
    
    @Autowired
    private ResponsResultInterceptor responsResultInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry interceptorRegistry) {
        // addPathPatterns("/**") 表示拦截所有的请求,
        // excludePathPatterns("/login", "/register") 表示除了登陆与注册之外,因为登陆注册不需要登陆也可以访问
        interceptorRegistry.addInterceptor(responsResultInterceptor).addPathPatterns("/**");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry resourceHandlerRegistry) {
        // TODO Auto-generated method stub

    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> arg0) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void addCorsMappings(CorsRegistry arg0) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void addFormatters(FormatterRegistry arg0) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> arg0) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void addViewControllers(ViewControllerRegistry arg0) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer arg0) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer arg0) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer arg0) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> arg0) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> arg0) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer arg0) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry arg0) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> arg0) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> arg0) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public MessageCodesResolver getMessageCodesResolver() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Validator getValidator() {
        // TODO Auto-generated method stub
        return null;
    }

}

3.新建返回枚举类,返回格式封装类,错误返回格式类

3.1 格式定义

package com.api.result;

public enum ResultCode {
    //成功
    SUCCESS(1,"成功"),
    //参数错误100-199
    PARAM_IS_INVALID(101,"参数无效"),
    PARAM_IS_BLANK(102,"参数为空"),
    PARAM_TYPE_BIND_ERROR(103,"参数类型错误"),
    PARAM_NOT_COMPLETE(104,"参数缺失"),
    //用户错误200-299
    USER_NOT_LOGGED_IN(201,"用户未登录,访问的路径需要验证,请登录"),
    USER_LOGIN_ERROR(202,"账号不存在或密码错误"),
    USER_ACCOUNT_FORBIDDEN(203,"账号已被禁用"),
    USER_NOT_EXIST(204,"用户不存在"),
    USER_HAS_EXISTED(205,"用户已存在");
    
    private Integer code;
    
    private String message;
    
    ResultCode(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
    
    public Integer code() {
        return this.code;
    }

    public String message() {
        return this.message;
    }
    
}

3.2 统一返回格式

package com.api.result;

import java.io.Serializable;

public class Result implements Serializable {
    
    private static final long serialVersionUID = 1L;

    private Integer code;
    
    private String message;

    private Object data;

    public Result() {
    }

    public Result(ResultCode resultCode, Object data) {
        this.code = resultCode.code();
        this.message = resultCode.message();
        this.data = data;
    }
    
    public static Result success(){
        Result result = new Result();
        result.setResultCode(ResultCode.SUCCESS);
        return result;
    }
    
    private void setResultCode(ResultCode resultCode) {
        this.code = resultCode.code();
        this.message = resultCode.message();
    }

    public static Result success(Object data){
        Result result = new Result();
        result.setResultCode(ResultCode.SUCCESS);
        result.setData(data);
        return result;
    }
    
    public static Result failure(ResultCode resultCode){
        Result result = new Result();
        result.setResultCode(resultCode);
        return result;
    }
    
    public static Result failure(ResultCode resultCode, Object data){
        Result result = new Result();
        result.setResultCode(resultCode);
        result.setData(data);
        return result;
    }
    
    public static Result failure(Integer code, String message, Object errors) {
        Result result = new Result();
        result.setCode(code);
        result.setMessage(message);
        result.setData(errors);
        return result;
    }

    public Integer getCode() {
        return code;
    }

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

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getData() {
        return data;
    }

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

}

3.3 异常返回格式

package com.api.result;

public class ErrorResult {
    
    private Integer code;
    
    private String message;
    
    private Object errors;

    public Integer getCode() {
        return code;
    }

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

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getErrors() {
        return errors;
    }

    public void setErrors(Object errors) {
        this.errors = errors;
    }

}

3.4 新建ResponseResultHandle 类实现ResponseBodyAdvice接口,用于实现自定义返回类型,全局异常处理

package com.api.handler;

import javax.servlet.http.HttpServletRequest;

import org.springframework.core.MethodParameter;
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.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import com.api.cache.ResponseURICache;
import com.api.result.ErrorResult;
import com.api.result.Result;

@ControllerAdvice
public class ResponseResultHandle implements ResponseBodyAdvice<Object> {

    //是否请求包含了包装注解标记,没有就直接返回,不需要重写返回体
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = sra.getRequest();
        //判断请求是否有包装标记
        return ResponseURICache.getInstance().get(request.getRequestURI());
    }
    
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
            Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        //进入返回体重写格式
        if(body instanceof ErrorResult){
            //返回值异常,作包装处理
            ErrorResult errorResult = (ErrorResult) body;
            return Result.failure(errorResult.getCode(), errorResult.getMessage(), errorResult.getErrors());
        }
        return Result.success(body);
    }
    
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ErrorResult handleRuntimeException(Exception e){
        ErrorResult errorResult = new ErrorResult();
        errorResult.setCode(500);
        errorResult.setMessage("服务器异常");
        errorResult.setErrors(errorResult.getErrors());
        return errorResult;
    }

}

4.新建缓存类

可以根据具体需求使用缓存数据库代替缓存类,这里使用一个用hashMap实现缓存类

package com.api.cache;

import java.util.HashMap;
import java.util.Map;

public class ResponseURICache {
    
    //缓存map
    private Map<String, Boolean> cacheMap = new HashMap<String, Boolean>();
    
    //禁止指令重排优化
    private static volatile ResponseURICache URICACHE;
    
    private ResponseURICache(){}

    public static ResponseURICache getInstance(){
        if(null == URICACHE){
            synchronized(ResponseURICache.class){
                if(null == URICACHE){
                    URICACHE = new ResponseURICache();
                }
            }
        }
        return URICACHE;
    }

    /**
     *  设置缓存值
     * @param key
     * @param value
     */
    public void set(String key, Boolean value) {
        cacheMap.put(key, value);
    }

    /**
     *  获取缓存key的value值
     * @param key
     * @return
     */
    public Boolean get(String key) {
        if (!cacheMap.containsKey(key)) {
            return null;
        }
        return cacheMap.get(key);
    }
}

5.Web控制层添加注解

package com.api.controll;

import java.util.HashMap;
import java.util.Map;

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

import com.api.annotation.ResponseResult;

@RestController
@ResponseBody
public class BookControll {
    
    @GetMapping("book")
    @ResponseResult
    public Object getBook(){
        Map<String, String> map = new HashMap<String, String>();
        map.put("title", "倾城之恋");
        map.put("author", "张爱玲");
        return map;
    }

    @GetMapping("/book/name")
    public Object getBookById(){
        Map<String, String> map = new HashMap<String, String>();
        map.put("title", "金锁记");
        map.put("author", "张爱玲");
        return map;
    }
    
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容