美文网首页程序员
SpringBoot Web应用范例

SpringBoot Web应用范例

作者: 圆圆仙人球 | 来源:发表于2017-09-17 17:19 被阅读0次

    本文主要是介绍一个基于SpringBoot的Web应用范例,范例中根据自己多年的工作经验及总结提供了一些使用建议,希望对大家有所启发

    • 代码结构概览
      代码结构很简单,就是很常见的三层架构,通过包名来清晰的展现该种层次结构:


      image.png
    • Web API封装
      Web API层主要是给前端提供web api接口,我们规定了,所有的api接口响应报文必须是如下结构体:

    public class Response<T> {
    
        private int status;
        private String msg;
        private T data;
    
        public Response() {
        }
    
        public Response(int status, String msg, T data) {
            this.status = status;
            this.msg = msg;
            this.data = data;
        }
    
        public int getStatus() {
            return status;
        }
    
        public void setStatus(int status) {
            this.status = status;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    }
    

    其中 status表明接口状态码(成功/失败/其他...,0表示成功), msg是对状态码的简单文字描述(比如:success、失败原因 等等), data是接口返回的具体数据

    对返回报文格式进行统一规定,有利于前端做一些公共逻辑:比如对非0状态码记录console日志、对一些特定状态码执行一些统一逻辑等等

    返回统一报文格式是通过AOP来实现的,并不需要在业务代码中转换成Response实体,封装代码如下:

    1. 接口正常返回的情况:
    @Order(1)
    @ControllerAdvice(basePackages = AppContants.API_BASE_PACKAGE)
    public class ApiResponseBodyAdvice implements ResponseBodyAdvice<Object> {
    
        private static final Logger logger = LoggerFactory.getLogger(ApiResponseBodyAdvice.class);
    
        @Override
        public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
            return true;
        }
    
        @Override
        public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                        Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
                        ServerHttpResponse response) {
            Response<Object> resp = new Response<>();
            resp.setStatus(0);
            resp.setMsg("success");
            resp.setData(body);
    
            if(returnType.getMethod().getReturnType() == String.class){
                return JSON.toJSONString(resp);
            }else{
                return resp;
            }
        }
    }
    
    1. 接口抛异常的情况:
    @ControllerAdvice(basePackages = AppContants.API_BASE_PACKAGE)
    public class ApiExceptionHandler {
    
        private Logger logger = LoggerFactory.getLogger(getClass());
    
        private static final int UNKNOWN_EXCEPTION_STATUS = -2;
        private static final int ILLEGAL_ARGUMENT_STATUS = -1;
    
        @ExceptionHandler(value = Exception.class)
        @ResponseBody
        public Response defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
            Response response = new Response();
            if (e instanceof AppException) {
                response.setStatus(((AppException) e).getCode());
                response.setMsg(e.getMessage());
            } else if (e instanceof IllegalArgumentException) {
                response.setStatus(ILLEGAL_ARGUMENT_STATUS);
                response.setMsg(e.getMessage());
            } else {
                logger.error("Got exception,url=" + req.getRequestURI(), e);
                response.setStatus(UNKNOWN_EXCEPTION_STATUS);
                response.setMsg("接口异常");
            }
            return response;
        }
    }
    

    经过这样的封装后,业务代码中不再涉及Response实体的转换,如下例子所示:

    @RestController
    @Api(description = "用户相关接口")
    @RequestMapping("/api/user")
    public class UserEndpoint {
    
        private Logger logger = LoggerFactory.getLogger(getClass());
    
        @Autowired
        private UserService userService;
    
        @ApiOperation(value = "查询用户", notes = "根据用户id查询用户详情")
        @GetMapping("/{userId}")
        public User getUser(@PathVariable("userId") Integer userId) {
            Preconditions.checkNotNull(userId, "user id not provided");
            Preconditions.checkArgument(userId > 0, "user id must greater than 0");
    
            return this.userService.getUser(userId);
        }
    
        @ApiOperation(value = "更新用户信息")
        @PostMapping("/update")
        public void updateUser(@RequestBody User user) {
            logger.info("User:{}", user);
        }
    }
    

    在这里简单说一下,上面代码有几句关于参数校验的代码(如下所示),我们只需要简单用Preconditions工具类来进行判断,如果条件不满足将会抛出IllegalArgumentException,这个未捕获的异常将会在ApiExceptionHandler得到处理 并将返回status设置为-1,msg设置为异常信息。这样处理后显得代码特别简洁 有木有~

    Preconditions.checkNotNull(userId, "user id not provided");
    Preconditions.checkArgument(userId > 0, "user id must greater than 0");
    
    • Swagger API文档
      关于Swagger的介绍,网上有很多,不了解的可以看看:Spring Boot中使用Swagger2构建强大的RESTful API文档

      Springboot 可以很简单地就把swagger 集成到应用中,通过swagger api文档,可以减少前后端的沟通成本,并且也给后端人员提供了便捷的调试接口的方式,非常推荐使用

    • Github 源代码
      本文中所涉及的完整的代码已经放到github上,有兴趣的童鞋可以看看:springboot-web-showcase

    相关文章

      网友评论

        本文标题:SpringBoot Web应用范例

        本文链接:https://www.haomeiwen.com/subject/rtqmsxtx.html