5. spring-boot REST 全局異常處理

講點(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)很大的麻煩,故此為止:

  1. 全局異常捕捉及處理
  2. 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工具類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é)果類似{"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ě)的:

  1. GET /api/users,獲取全部
  2. POST /api/users,新增一個(gè)
  3. GET /api/users/:id,獲取單個(gè)
  4. DELETE /api/users/:id,刪除單個(gè)
  5. PUT /api/users/:id,全量更新
  6. 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)椴恍枰?,具體請(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è)圖

新增一個(gè)用戶,kaenry/jianshu
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,812評(píng)論 25 709
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 34,628評(píng)論 18 399
  • 深情既是死罪,又怎怕挫骨揚(yáng)灰。 或許感動(dòng)你了嗎? 就感覺(jué)觸及了心最柔軟的地方,或許我感動(dòng)了
    青草王閱讀 297評(píng)論 0 0
  • —title: “也談《大話西游3》,經(jīng)典永不毀”— 說(shuō)明 首先,我得承認(rèn),我還沒(méi)有觀看《大話西游3》這部影片,而...
    何建博桑閱讀 801評(píng)論 1 4

友情鏈接更多精彩內(nèi)容