講點(diǎn)實(shí)用的小技巧屑彻,學(xué)習(xí)前端之后才發(fā)現(xiàn)以前寫(xiě)的代碼真是給前端兒搞了不少事验庙,在此誠(chéng)懇道歉
單頁(yè)應(yīng)用越來(lái)越多以及移動(dòng)化之后,服務(wù)化已經(jīng)是老生常談了社牲,在前文代碼的基礎(chǔ)上做些簡(jiǎn)單的通用模塊的處理粪薛,后端返回結(jié)果的不一致性真的會(huì)給前端帶來(lái)很大的麻煩,故此為止:
- 全局異常捕捉及處理
- REST FULL基本常見(jiàn)規(guī)范
直接貼核心代碼搏恤。統(tǒng)一返回結(jié)果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工具類(lèi)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);
}
}
統(tǒng)一異常攔截處理: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);
}
}
無(wú)論請(qǐng)求成功或失敗統(tǒng)一返回RestResult
违寿,可自由定義,比如加上錯(cuò)誤code或異常的多次處理以及日志啊什么的熟空,代碼都很簡(jiǎn)單藤巢,這里就不詳細(xì)介紹了,返回的結(jié)果類(lèi)似{"result":true,"message":null,"data":{"id":3,"username":"kaenry","password":"jianshu"}}
息罗,spring-boot
默認(rèn)使用Jackson
解析拼裝json
掂咒,如需要忽略null
,加個(gè)注解即可:@JsonInclude(JsonInclude.Include.NON_NULL)
,fastjson
默認(rèn)開(kāi)啟迈喉。@Valid
注解會(huì)驗(yàn)證屬性绍刮,不通過(guò)會(huì)先交給BindingResult
,如果沒(méi)有這個(gè)參數(shù)則會(huì)拋出異常MethodArgumentNotValidException
弊添,@ExceptionHandler
捕捉到異常則會(huì)進(jìn)入illegalParamsExceptionHandler
方法返回結(jié)果:
{
"result": false,
"message": "request params invalid"
}
測(cè)試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標(biāo)準(zhǔn)書(shū)寫(xiě)的:
-
GET /api/users
录淡,獲取全部 -
POST /api/users
捌木,新增一個(gè) -
GET /api/users/:id
油坝,獲取單個(gè) -
DELETE /api/users/:id
,刪除單個(gè) -
PUT /api/users/:id
刨裆,全量更新 -
PATCH /api/users/:id
澈圈,部分更新
代碼都很簡(jiǎn)單,注意參數(shù)盡量使用Bean
帆啃,非特殊情況千萬(wàn)不要使用諸如Map
作為接收參數(shù)瞬女,圖一時(shí)痛快,飲恨一生芭恕诽偷;在這里使用@RequestBody
的原因是因?yàn)楝F(xiàn)在的前端(因?yàn)橛辛?code>nodejs)大多都會(huì)采用JSON
直傳而不是傳統(tǒng)意義上的form
了,對(duì)應(yīng)其實(shí)就是http
協(xié)議里的請(qǐng)求頭從application/x-www-form-urlencoded
換成了application/json
疯坤;這里在更新的時(shí)候有個(gè)小技巧报慕,使用BeanUtils
復(fù)制需要的屬性,getNullPropertyNames
方法是返回對(duì)象里面的為null
的屬性压怠,因?yàn)椴恍枰旅吒裕唧w請(qǐng)看代碼。地址還是那個(gè)地址:https://github.com/kaenry/spring-boot-magneto/releases/tag/v1.8.2菌瘫。
畢竟不是真實(shí)項(xiàng)目蜗顽,沒(méi)有寫(xiě)測(cè)試布卡,測(cè)試工具推薦使用PostMan
插件,記得先獲取token
雇盖,隨便上個(gè)圖