服务器后台设计API接口时,目前最流行的风格(原则/标准/规范)就是RESTful,往往简称为REST。
其中 REST=REpresentational State Transfer
REST直译:表现层状态转移
REST核心含义:无状态的资源 资源的变化(CURD)都是通过操作去实现的 资源可以用 URI 表示 用不同的URI和方法,表示对资源的不同操作
简单的说,就是使用同一个URI,就能实现增删改查的请求。而区别增删改查则需要以下典型的Rest方法:
Spring对Restful有很好的支持,提供了以下注解分别对应上述的方法。
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
那么我们走一遍增删改查。通过以下例子可以看出,不管增删改查,URI都是http://localhost:8081/test/companies/。这样对你的程序设计,维护,文档设计,维护都很有帮助。
前端请求url例子
http://localhost:8081/test/companies/任意公司ID 复制代码
后端接收代码。这里要讲解的就是,@GetMapping中的{companyId}代表具体的公司id,配合@PathVariable注解,就能够将URL中的字符串提取成参数变量。
@GetMapping(value="/companies/{companyId}") public ResponseDto getCompany(@PathVariable("companyId") int companyId) { return service.getCompany(companyId); } 复制代码
前端请求url例子
http://localhost:8081/test/companies/ 复制代码
如果用postman测试,你需要包含一段json,作为你要插入的数据。
后端代码。因为你的接收数据在body中,所以实体类前必须添加@RequestBody注解。当然了,你也可以进行数据校验,并把结果放入BindingResult。
@PostMapping(value="/companies") public ResponseDto regitsCompany(@Valid @RequestBody MCompany dto, BindingResult result) { return service.registCompany(dto); } 复制代码
前端URL例子。跟2.2中的post一样,body中传递json。这里就不截图了。
http://localhost:8081/test/companies/任意公司ID 复制代码
后端代码。这里又有一个变通,就是既需要使用@PathVariable,又要使用@RequestBody,才能把url中的值跟json的值同时取到。
@PutMapping(value="/companies/{companyId}") @Transactional public ResponseDto updateCompany(@PathVariable("companyId") int companyId, @Valid @RequestBody MCompany dto, BindingResult result) { dto.setCompanyId(companyId); return service.updateCompany(dto); } 复制代码
前端URL例子
http://localhost:8081/test//companies/任意公司ID 复制代码
后端代码。这个就没什么好讲的了。
@Transactional @DeleteMapping("/companies/{companyId}") public ResponseDto deleteCompany(@PathVariable("companyId") int companyId) { return service.deleteCompany(companyId); } 复制代码
上述controller中ResponseDto的代码是这样的。
@Data @Component public class ResponseDto { // 状态码 private String code; // 状态信息 private String msg; // 响应数据 private Object data; } 复制代码
这是Restful返回的比较标准的格式。
发生异常的话,有可能是数据校验出错,那样压根不进入controller方法体。也有可能在其他地方出错,比如进入方法体后的片段中出错,我们应该统一在ExceptionHander中处理。
像下面这样,我们返回ResponseEntity,并与HttpStatus关联,当然可以是400,404,500。ResponseDto中你可以放同样的code,也可以是你自己自定义的错误码。这里我们为了方便就设置成同样的。
比如请求中的邮件地址非法,就会进入ValidationException的分支,出现上述图片的错误。
@RestControllerAdvice public class GlobalExceptionController { Logger logger = LogManager.getLogger(GlobalExceptionController.class); @Autowired ResponseDto res; private ResponseEntity<ResponseDto> buildResponse(Exception e, HttpStatus status) { logger.error(e.getMessage()); res.setCode(String.valueOf(status.value())); res.setMsg(e.getMessage()); res.setData(""); return new ResponseEntity<ResponseDto>(res, status); } // 数据校验错误 @ExceptionHandler(ValidationException.class) public ResponseEntity<ResponseDto> handleError(ValidationException e) { return buildResponse(e, HttpStatus.BAD_REQUEST); } // 文件IO错误 @ExceptionHandler(FileException.class) public Object handleError(FileException e) { return buildResponse(e, HttpStatus.INTERNAL_SERVER_ERROR); } // 业务逻辑错误 @ExceptionHandler(SiLEDBusinessException.class) public Object handleError(SiLEDBusinessException e) { return buildResponse(e, HttpStatus.BAD_REQUEST); } // 其他错误 @ExceptionHandler(Exception.class) public Object handleError(Exception e) { return buildResponse(e, HttpStatus.INTERNAL_SERVER_ERROR); } } 复制代码
如果你想精准的匹配URL,可以使用正则表达式。
@RequestMapping(value = "/{name:([a-z][0-9a-z-]{3,31}}/mytest",method = {RequestMethod.GET}) public void test(@PathVariable String name){ } 复制代码
上面没有讲到一种patch请求,该请求也是更新请求,但是是局部更新。什么是局部更新?加入有个user类,你更新他的所有属性,就用put。只更新username可以用put,但是显得很大题小作,因为put是全面替换。这时可以使用patch,仅更新username。
如果涉及到跨域,在请求Restful之前,浏览器会先发送一个Options请求给后台,询问该接口/端点支持哪些方法。所以需要配置该许可。
<servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!-- OPTIONS请求许可 --> <param-name>dispatchOptionsRequest</param-name> <param-value>true</param-value> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> 复制代码