网络程序正朝着移动设备的方向发展,前后端分离、APP,最好的交互交互方式莫过于通过API接口实现。
本项目加密方式采用参数排序+key验签方式,后期可按需求更换
本次我们先了解一下Spring对API接口开发的支持,然后我们采用Spring Boot搭建项目,本项目暂未使用权限管理系统,后期主键完善,可按需求对key进行配置或者使用安全框架进行管理。借用Swagger列出API接口,便于查阅。
根据当前趋势,API接口要求返回的格式一般为 application/json,有特殊行业如银行等返回格式为xml报文,本次统一使用json。Spring Boot返回json,提供了两种实现方式:类注解 和 方法注解。
类注解 @RestController
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/saveUser")
public Object saveUser(@Validated User user){
return user;
}
}
方法注解 @ResponseBody
@Controller
@RequestMapping("/demo")
public class Demo {
@RequestMapping
@ResponseBody
public String getCapitalize(String args){
return args.toUpperCase();
}
}
值得提醒的是,虽然都是都可以,但我更推荐使用类注解,项目的主要目的是提供和统一api接口,会显得我们的编码风格十分统一,代码更加紧凑,不至于看起来零散。
注:Mapping根据业务自行选择,推荐@GetMapping、@PostMapping格式。
@GetMapping("/saveUser")
public Object saveUser(@Validated User user){
return user;
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-validationartifactId>
dependency>
在实体参数上添加注解来达到验证参数的效果,然后在方法参数前添加@Validated开启验证。如果参数错误会抛出BindException异常,后期统一异常处理响应。
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)//解决参数为null不返回前端
public class User implements Serializable {
private Long Id;
@NotNull(message = "用户名不能为空")
private String username;
@NotNull(message = "年龄不能为空")
private Integer age;
private String email;
}
每个过程都单独处理异常,系统的代码耦合度高,工作量大且不好统一,维护的工作量也很大。 为了将所有类型的异常处理从各处理过程解耦出来,保证相关处理过程的功能较单一,实现异常信息的统一处理和维护。
Spring MVC处理异常有3种方式:
使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver;
实现Spring的异常处理接口HandlerExceptionResolver 自定义自己的异常处理器;
使用@ExceptionHandler注解实现异常处理;
今天主要说springboot基于@ExceptionHandler注解实现异常处理
@ExceptionHandler注解:定义控制器发生异常后的操作,可以拦截所有控制器发生的异常。统一异常处理 ,通过@ExceptionHandler(value = Exception.class) 来指定捕获的异常。“@ControllerAdvice + @ExceptionHandle" 可以处理除“404”以外的运行异常。
package com.cch.error;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Auther: cch
* @Date: 2020/9/21 16:03
* @Description: 自定义异常返回结果实体类
*/
@Data
@NoArgsConstructor
public class BaseBusinessException extends RuntimeException{
private BaseError error = DefaultError.SYSTEM_INTERNAL_ERROR;
private String extMessage = null;
public BaseBusinessException(String message) {
super(message);
this.extMessage = message;
}
public BaseBusinessException(String message, Throwable cause) {
super(message, cause);
this.extMessage = message;
}
public BaseBusinessException(Throwable cause) {
super(cause);
}
public BaseBusinessException(BaseError error) {
this.error = error;
}
public BaseBusinessException(String message, BaseError error) {
super(message);
this.extMessage = message;
this.error = error;
}
public BaseBusinessException(String message, Throwable cause, BaseError error) {
super(message, cause);
this.extMessage = message;
this.error = error;
}
public BaseBusinessException(Throwable cause, BaseError error) {
super(cause);
this.error = error;
}
}
package com.cch.error;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
/**
* @Auther: cch
* @Date: 2020/9/21 16:12
* @Description: 相应结果实体类
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Response {
private String code;
private Object data;
private String message;
}
package com.cch.exception;
import com.cch.error.BaseBusinessException;
import com.cch.error.DefaultError;
import com.cch.error.Response;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* @Auther: cch
* @Date: 2020/9/21 14:07
* @Description: 全局处理异常
*/
@RestControllerAdvice
public class ExceptionControllerAdvice {
@ExceptionHandler(Exception.class)
public ResponseEntity<Response> APIExceptionHandler(Exception exception) {
Response response = new Response();
if (exception instanceof BaseBusinessException) {
BaseBusinessException bbe = (BaseBusinessException)exception;
response.setCode(bbe.getError().getErrorCode());
response.setData(bbe.getError().getErrorMessage());
if (exception.getMessage() != null) {
response.setData(exception.getMessage());
}
} else if (exception instanceof BindException) {
BindException bindException = (BindException)exception;
response.setCode(DefaultError.PARAMETER_ERROR.getErrorCode());
response.setData(DefaultError.PARAMETER_ERROR.getErrorMessage());
FieldError fieldError = bindException.getBindingResult().getFieldError();
response.setMessage(fieldError.getDefaultMessage());
}else{
exception.printStackTrace();
response.setCode(DefaultError.SYSTEM_INTERNAL_ERROR.getErrorCode());
response.setData(DefaultError.SYSTEM_INTERNAL_ERROR.getErrorMessage());
}
return new ResponseEntity (response, HttpStatus.LOOP_DETECTED);
}
}
当项目中又异常可直接抛出,示例:
@GetMapping("/saveUser")
public Object saveUser(@Validated User user){
if(user == null){
throw new BaseBusinessException("用户不能为空");
}
return user;
}
附:异常使用枚举,可根据个人需求灵活使用,这是我个人使用习惯。
BaseError接口
package com.cch.error;
/**
* @Auther: cch
* @Date: 2020/9/21 16:06
* @Description: 异常枚举父接口
*/
public interface BaseError {
String getErrorCode();
String getErrorMessage();
}
可根据异常类型定义多个枚举,然后实现BaseError,示例通用异常:
package com.cch.error;
/**
* @Auther: cch
* @Date: 2020/9/21 16:07
* @Description: 默认异常
*/
public enum DefaultError implements BaseError {
SYSTEM_INTERNAL_ERROR("0000", "系统内部错误"),
PARAMETER_ERROR("0001","参数错误");
String errorCode;
String errorMessage;
private static final String ns = "DFT";
DefaultError(String errorCode, String errorMessage) {
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
@Override
public String getErrorCode() {
return ns + "." + errorCode;
}
@Override
public String getErrorMessage() {
return errorMessage;
}
}
注:下一篇link请求加密验签。