数据接口请求异常:error_springboot2.2.X手册:构建多元化的API接口,我们这样子设计

9d697554c697ad6543ec89dbf71c29da.png

无规矩不成方圆,任何一个软件,如果刚开始没有定义好规范,任由各个开发进行按照自己的喜好进行开发,后面运维的兄弟,估计整天就要骂娘了。

开发一时爽,运维火葬场,运维一个软件,往往比开发一个软件要辛苦好多,毕竟很多时候,运维都要从不明白需求,不理解系统架构,不理解数据结构的0开始。

今天来做一个定义多业务的接口规范,考虑到每家企业的业务不一样,只提供参考

6b477a58058527cf7db994fcf09dcb3c.png

定义多业务接口

在后端的服务中,我们的服务只会提供API接口,以便提供数据,根据业务需求,我们把接口分为三类

1、后端系统服务接口view,主要提供给后台管理系统使用,面向B端

2、前端系统服务接口application,主要提供给小程序,H5使用,面向C端

3、第三方服务接口external,主要提供给第三方使用

源码参考请关注后私信@溪云阁

d0372e908bd33f3950198ec85edf6a4d.png

统一接口消息体

/** * 消息影响实体 * @author:溪云阁 * @date:2020年5月2日 */@Data@Builder@NoArgsConstructor@AllArgsConstructor@ApiModel(value = "响应类", description = "响应类")public class ResponseMsg {    @ApiModelProperty(value = "响应状态", example = "00,成功;01,失败")    private String respStatus;    @ApiModelProperty(value = "响应描述", example = "例如:找不到用户信息")    private Object respDesc;    @ApiModelProperty(value = "响应实体")    private T data;}

定义全局异常

1、统一异常

/** * 全局公共异常类 * @author:溪云阁 * @date:2020年5月2日 */@Slf4jpublic class CommonRuntimeException extends RuntimeException {    private static final long serialVersionUID = 1L;    @Setter    @Getter    private Object[] params;    public CommonRuntimeException(Throwable e, Object... params) {        super(e);        this.params = params;    }    public CommonRuntimeException(Object... params) {        this(null, params);    }    public CommonRuntimeException(Throwable e) {        this(null, e);    }    public CommonRuntimeException() {    }    @Override    public String getMessage() {        log.error("获取到的错误信息:{}", super.getMessage());        log.error("获取到的错误内容:{}", super.fillInStackTrace());        return super.getMessage();    }    /**     * 重写fillInStackTrace 业务异常不需要堆栈信息,提高效率.     * @author 溪云阁     * @return     */    @Override    public Throwable fillInStackTrace() {        return this;    }}

2、统一异常处理

/** * 统一异常处理 * @author:溪云阁 * @date:2020年5月2日 */@RestControllerAdvicepublic class CommonExceptionHandler {    /**     * 对错误信息进行处理     * @author 溪云阁     * @param e 拦截到的错误信息     * @return     * @throws Exception ResponseMsg     */    @ExceptionHandler(value = Exception.class)    public ResponseMsg commonErrorHandler(Exception e) throws Exception {        System.out.println("进入到异常测试");        ResponseMsg msg = new ResponseMsg<>();        if (e instanceof CommonRuntimeException) {            final CommonRuntimeException common = (CommonRuntimeException) e;            msg = MsgUtils.buildFailureMsg(common.getMessage());        } else if (e instanceof MethodArgumentNotValidException) {            final MethodArgumentNotValidException valid = (MethodArgumentNotValidException) e;            msg = MsgUtils.buildFailureMsg(buildParamError(valid.getBindingResult()));        } else {            msg = MsgUtils.buildFailureMsg(e.getMessage());        }        return msg;    }    /**     * 构建参数错误     * @author 溪云阁     * @param result     * @return JSONObject     */    private JSONObject buildParamError(BindingResult result) {        JSONObject json = null;        if (result.hasErrors()) {            json = new JSONObject();            final List errors = result.getFieldErrors();            for (final FieldError error : errors) {                json.put(error.getField(), error.getDefaultMessage());            }        }        return json;    }}

构建拦截的信息体

1、日志拦截

/** * 请求日志 * @author:溪云阁 * @date:2020年5月2日 */@Aspect@Component@Slf4jpublic class RequestLogs {    /**     * 拦截手机端请求接口     * @author 溪云阁 void     */    @Pointcut("execution( * com.boots.*.application..application.*.*(..))")    public void applicationPointCut() {    }    /**     * 拦截电脑端请求接口     * @author 溪云阁 void     */    @Pointcut("execution( * com.boots.*.view..view.*.*(..))")    public void webPointCut() {    }    /**     * 拦截外部/第三方请求接口     * @author 溪云阁 void     */    @Pointcut("execution( * com.boots.*.external..external.*.*(..))")    public void externalPoint() {    }    /**     * 请求前记录信息     * @author 溪云阁     * @param joinPoint     * @throws Throwable void     */    @Before("applicationPointCut() || webPointCut() || externalPoint()")    public void doBefore(JoinPoint joinPoint) throws Throwable {        // 接收到请求,记录请求内容        final HttpServletRequest request = HttpContextUtils.getHttpServletRequest();        // 记录下请求内容        log.info("请求地址 : " + request.getRequestURL().toString());        log.info("请求方法 : " + request.getMethod());        log.info("类方法 : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());        log.info("参数 : " + Arrays.toString(joinPoint.getArgs()));    }    /**     * 请求后获取返回值     * @author 溪云阁     * @param ret     * @throws Throwable void     */    @AfterReturning(returning = "ret", pointcut = "applicationPointCut() || webPointCut() || externalPoint()")    public void doAfterReturning(Object ret) throws Throwable {        // 处理完请求,返回内容(返回值太复杂时,打印的是物理存储空间的地址)        log.info("返回值 : " + ret);    }    /**     * 请求打印耗时时间,方便出问题的时候做定位     * @author 溪云阁     * @param pjp     * @return     * @throws Throwable Object     */    @Around("applicationPointCut() || webPointCut() || externalPoint()")    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {        final long startTime = System.currentTimeMillis();        // ob 为方法的返回值        final Object ob = pjp.proceed();        log.info("耗时 : " + (System.currentTimeMillis() - startTime));        return ob;    }}

2、限流拦截

/** * 请求限流 * @author:溪云阁 * @date:2020年5月2日 */@Aspect@Component@Slf4jpublic class RequestLimit {    // 每秒产生150个令牌    private static final RateLimiter RATE_LIMITER = RateLimiter.create(150);    /**     * 拦截移动端请求接口     * @author 溪云阁     * void     */    @Pointcut("execution( * com.atomic.*.application..application.*.*(..))")    public void applicationPointCut() {    }    /**     * 拦截电脑端请求接口     * @author 溪云阁     * void     */    @Pointcut("execution( * com.atomic.*.web..controller.*.*(..))")    public void webPointCut() {    }    /**     * 拦截外部/第三方请求接口     * @author 溪云阁     * void     */    @Pointcut("execution( * com.atomic.*.external..external.*.*(..))")    public void externalPoint() {    }    @Before("applicationPointCut() || webPointCut() || externalPoint()")    public void doBefore(JoinPoint joinPoint) throws Throwable {    }    @AfterReturning(returning = "ret", pointcut = "applicationPointCut() || webPointCut() || externalPoint()")    public void doAfterReturning(Object ret) throws Throwable {    }    @Around("applicationPointCut() || webPointCut() || externalPoint()")    public Object doAround(ProceedingJoinPoint pjp) {        Object obj = null;        try {            final HttpServletRequest request = HttpContextUtils.getHttpServletRequest();            final HttpServletResponse response = HttpContextUtils.getHttpServletResponse();            if (RATE_LIMITER.tryAcquire()) {                // 执行方法                obj = pjp.proceed();            } else {                // 拒绝了请求(服务降级)                log.info("拒绝了请求:" + request.getRequestURI());                response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);                response.setContentType("application/json;charset=UTF-8");                response.getOutputStream().write(MsgUtils.buildFailureMsg("服务器繁忙,稍后再试").toString().getBytes("utf-8"));            }        }        catch (final Throwable e) {            log.error("限流出现问题:", e.fillInStackTrace());            throw new CommonRuntimeException(e.getMessage());        }        return obj;    }}

整合swagger接口可视化

/** * swagger配置 * @author:溪云阁 * @date:2020年5月2日 */@EnableSwagger2@Configurationpublic class SwaggerConfig implements WebMvcConfigurer {    /**     * 后台服务接口     * @author 溪云阁     * @date 2019年5月22日     * @return Docket     */    @Bean    public Docket webApi() {        return new Docket(DocumentationType.SWAGGER_2)                .groupName(ApiConstant.SW_TITLE_WEB)                .genericModelSubstitutes(DeferredResult.class)                .useDefaultResponseMessages(false)                .forCodeGeneration(true)                .select()                .apis(RequestHandlerSelectors.basePackage(ApiConstant.SW_PACKPATH_WEB))                .paths(PathSelectors.regex(".*/view/.*"))                .build()                .apiInfo(new ApiInfoBuilder()                        // 页面标题                        .title(ApiConstant.SW_TITLE_WEB)                        // 创建人                        .contact(new Contact(ApiConstant.SW_AUTHOR, ApiConstant.SW_EMAIL, ApiConstant.SW_URL))                        // 版本号                        .version(ApiConstant.SW_VERSION)                        // 描述                        .description(ApiConstant.SW_DESCRIPTION_WEB)                        .build());    }    /**     * 前台服务接口     * @author 溪云阁     * @return Docket     */    @Bean    public Docket appApi() {        return new Docket(DocumentationType.SWAGGER_2)                .groupName(ApiConstant.SW_TITLE_APPLICATION)                .genericModelSubstitutes(DeferredResult.class)                .useDefaultResponseMessages(false)                .forCodeGeneration(true)                .select()                .apis(RequestHandlerSelectors.basePackage(ApiConstant.SW_PACKPATH_APPLICATION))                .paths(PathSelectors.regex(".*/application/.*"))                .build()                .apiInfo(new ApiInfoBuilder()                        // 页面标题                        .title(ApiConstant.SW_TITLE_APPLICATION)                        // 创建人                        .contact(new Contact(ApiConstant.SW_AUTHOR, ApiConstant.SW_EMAIL, ApiConstant.SW_URL))                        // 版本号                        .version(ApiConstant.SW_VERSION)                        // 描述                        .description(ApiConstant.SW_DESCRIPTION_APPLICATION)                        .build());    }    /**     * 第三方服务接口     * @author 溪云阁     * @return Docket     */    @Bean    public Docket externalApi() {        return new Docket(DocumentationType.SWAGGER_2)                .groupName(ApiConstant.SW_TITLE_EXTERNAL)                .genericModelSubstitutes(DeferredResult.class)                .useDefaultResponseMessages(false)                .forCodeGeneration(true)                .select()                .apis(RequestHandlerSelectors.basePackage(ApiConstant.SW_PACKPATH_EXTERNAL))                .paths(PathSelectors.regex(".*/external/.*"))                .build()                .apiInfo(new ApiInfoBuilder()                        // 页面标题                        .title(ApiConstant.SW_TITLE_EXTERNAL)                        // 创建人                        .contact(new Contact(ApiConstant.SW_AUTHOR, ApiConstant.SW_EMAIL, ApiConstant.SW_URL))                        // 版本号                        .version(ApiConstant.SW_VERSION)                        // 描述                        .description(ApiConstant.SW_DESCRIPTION_EXTERNAL)                        .build());    }    /**     * 所有服务接口     * @author 溪云阁     * @return Docket     */    @Bean    public Docket allApi() {        return new Docket(DocumentationType.SWAGGER_2)                .groupName(ApiConstant.SW_TITLE_ALL)                .genericModelSubstitutes(DeferredResult.class)                .useDefaultResponseMessages(false)                .forCodeGeneration(true)                .select()                .apis(RequestHandlerSelectors.basePackage(ApiConstant.SW_PACKPATH_ALL))                .paths(PathSelectors.any())                .build()                .apiInfo(new ApiInfoBuilder()                        // 页面标题                        .title(ApiConstant.SW_TITLE_ALL)                        // 创建人                        .contact(new Contact(ApiConstant.SW_AUTHOR, ApiConstant.SW_EMAIL, ApiConstant.SW_URL))                        // 版本号                        .version(ApiConstant.SW_VERSION)                        // 描述                        .description(ApiConstant.SW_DESCRIPTION_ALL)                        .build());    }    /**     * 把swagger的静态页面添加到静态资源中,不然会出现404错误     * @author 溪云阁     * @date 2018年12月15日     * @param registry     */    @Override    public void addResourceHandlers(ResourceHandlerRegistry registry) {        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");    }}

运行

我们直接输入:localhost:8080/doc.html,即可看到我们的接口,在这里进行接口调试。

f5085c62525be5a9ad5acb88bf9e732d.png

源码请关注后私信

--END--

作者:@溪云阁

如需要源码,转发,关注后私信我。

部分图片或代码来源网络,如侵权请联系删除,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值