本文主要是介绍一个基于SpringBoot的Web应用范例,范例中根据自己多年的工作经验及总结提供了一些使用建议,希望对大家有所启发
-
代码结构概览
代码结构很简单,就是很常见的三层架构,通过包名来清晰的展现该种层次结构:
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实体,封装代码如下:
- 接口正常返回的情况:
@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;
}
}
}
- 接口抛异常的情况:
@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