SpringBoot Web应用范例

本文主要是介绍一个基于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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,065评论 25 708
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,356评论 11 349
  • 我想, 开成一枝海棠, 在赏花的人群里, 偷看你的模样。 我想, 开成一缕阳光, 透过半开的窗帘, 亲吻你的脸庞。...
    梦见月光阅读 238评论 0 2
  • 我的内心充斥着孤独。Sorrow. I am so sorrowful.
    我叫王小丫阅读 387评论 0 0
  • 孟子曰:仁义礼智。后经董仲舒发展成了:仁义礼智信。也就是我们后来讲的“五常”。这五常贯穿于中国民族伦理发展中...
    98波妞阅读 246评论 0 0