讲点实用的小技巧,学习前端之后才发现以前写的代码真是给前端儿搞了不少事,在此诚恳道歉
单页应用越来越多以及移动化之后,服务化已经是老生常谈了,在前文代码的基础上做些简单的通用模块的处理,后端返回结果的不一致性真的会给前端带来很大的麻烦,故此为止:
- 全局异常捕捉及处理
- REST FULL基本常见规范
直接贴核心代码。统一返回结果RestResult
public class RestResult<T> {
private boolean result;
private String message;
private T data;
private RestResult() {}
public static <T> RestResult<T> newInstance() {
return new RestResult<>();
}
// ...setter and getter
@Override
public String toString() {
return "RestResult{" +
"result=" + result +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
result工具类RestResultGenerator
/**
* Created by kaenry on 2016/9/20.
* RestResultGenerator
*/
public class RestResultGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(RestResultGenerator.class);
/**
* normal
* @param success
* @param data
* @param message
* @param <T>
* @return
*/
public static <T> RestResult<T> genResult(boolean success, T data, String message) {
RestResult<T> result = RestResult.newInstance();
result.setResult(success);
result.setData(data);
result.setMessage(message);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("generate rest result:{}", result);
}
return result;
}
/**
* success
* @param data
* @param <T>
* @return
*/
public static <T> RestResult<T> genSuccessResult(T data) {
return genResult(true, data, null);
}
/**
* error message
* @param message error message
* @param <T>
* @return
*/
public static <T> RestResult<T> genErrorResult(String message) {
return genResult(false, null, message);
}
/**
* error
* @param error error enum
* @param <T>
* @return
*/
public static <T> RestResult<T> genErrorResult(ErrorCode error) {
return genErrorResult(error.getMessage());
}
/**
* success no message
* @return
*/
public static RestResult genSuccessResult() {
return genSuccessResult(null);
}
}
统一异常拦截处理:RestExceptionHandler
/**
* Created by kaenry on 2016/9/20.
* RestExceptionHandler
*/
@ControllerAdvice(annotations = RestController.class)
public class RestExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(RestExceptionHandler.class);
@ExceptionHandler
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
private <T> RestResult<T> runtimeExceptionHandler(Exception e) {
LOGGER.error("---------> huge error!", e);
return RestResultGenerator.genErrorResult(ErrorCode.SERVER_ERROR);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
private <T> RestResult<T> illegalParamsExceptionHandler(MethodArgumentNotValidException e) {
LOGGER.error("---------> invalid request!", e);
return RestResultGenerator.genErrorResult(ErrorCode.ILLEGAL_PARAMS);
}
}
无论请求成功或失败统一返回RestResult
,可自由定义,比如加上错误code或异常的多次处理以及日志啊什么的,代码都很简单,这里就不详细介绍了,返回的结果类似{"result":true,"message":null,"data":{"id":3,"username":"kaenry","password":"jianshu"}}
,spring-boot
默认使用Jackson
解析拼装json
,如需要忽略null
,加个注解即可:@JsonInclude(JsonInclude.Include.NON_NULL)
,fastjson
默认开启。@Valid
注解会验证属性,不通过会先交给BindingResult
,如果没有这个参数则会抛出异常MethodArgumentNotValidException
,@ExceptionHandler
捕捉到异常则会进入illegalParamsExceptionHandler
方法返回结果:
{
"result": false,
"message": "request params invalid"
}
测试RestController
修改已有代码UserRestController
:
@RestController
@RequestMapping("/api/users")
public class UserRestController {
@Autowired
IUserService userService;
/**
* get all user, GET
* @return
*/
@RequestMapping(value = "", method = RequestMethod.GET)
public RestResult<List<User>> all() {
List<User> all = userService.findAll();
return RestResultGenerator.genSuccessResult(all);
}
/**
* add single user
* @param user username, password
* @return RestResult
* @throws Exception valid check
*/
@RequestMapping(value = "", method = RequestMethod.POST)
public RestResult<User> save(@Valid @RequestBody User user) throws Exception {
User save = userService.save(user);
return RestResultGenerator.genSuccessResult(save);
}
/**
* get single user by id, GET /id
* @param id user id
* @return RestResult<User>
* @throws Exception
*/
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public RestResult<User> get(@PathVariable Long id) throws Exception {
User user = userService.findById(id);
return RestResultGenerator.genSuccessResult(user);
}
/**
* delete user by id
* @param id user id
* @return success
* @throws Exception
*/
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public RestResult delete(@PathVariable Long id) throws Exception {
userService.delete(id);
return RestResultGenerator.genSuccessResult();
}
/**
* update user for all props
* @param id update user id
* @param newUser new props
* @return updated User
* @throws Exception
*/
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public RestResult<User> updateAll(@PathVariable Long id, @Valid @RequestBody User newUser) throws Exception {
User user = userService.findById(id);
// copy all new user props to user except id
BeanUtils.copyProperties(newUser, user, "id");
user = userService.save(user);
return RestResultGenerator.genSuccessResult(user);
}
/**
* update user for some props
* @param id update user id
* @param newUser some props
* @return updated user
* @throws Exception
*/
@RequestMapping(value = "/{id}", method = RequestMethod.PATCH)
public RestResult<User> update(@PathVariable Long id, @Valid @RequestBody User newUser) throws Exception {
User user = userService.findById(id);
// copy all new user props to user except null props
BeanUtils.copyProperties(newUser, user, Utils.getNullPropertyNames(newUser));
user = userService.save(user);
return RestResultGenerator.genSuccessResult(user);
}
}
尽量按照RESTFULL标准书写的:
-
GET /api/users
,获取全部 -
POST /api/users
,新增一个 -
GET /api/users/:id
,获取单个 -
DELETE /api/users/:id
,删除单个 -
PUT /api/users/:id
,全量更新 -
PATCH /api/users/:id
,部分更新
代码都很简单,注意参数尽量使用Bean
,非特殊情况千万不要使用诸如Map
作为接收参数,图一时痛快,饮恨一生啊;在这里使用@RequestBody
的原因是因为现在的前端(因为有了nodejs
)大多都会采用JSON
直传而不是传统意义上的form
了,对应其实就是http
协议里的请求头从application/x-www-form-urlencoded
换成了application/json
;这里在更新的时候有个小技巧,使用BeanUtils
复制需要的属性,getNullPropertyNames
方法是返回对象里面的为null
的属性,因为不需要更新,具体请看代码。地址还是那个地址:https://github.com/kaenry/spring-boot-magneto/releases/tag/v1.8.2。
毕竟不是真实项目,没有写测试,测试工具推荐使用PostMan
插件,记得先获取token
,随便上个图