国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

RESTful & “優雅的”API 響應結構設計

MingjunYang / 1577人閱讀

摘要:概述一個規范易懂和優雅,以及結構清晰且易于理解的響應結構,完全可以省去許多無意義的溝通和文檔。

概述

一個規范、易懂和優雅,以及結構清晰且易于理解的API響應結構,完全可以省去許多無意義的溝通和文檔。

預覽

操作成功:

{
    "status": true,
    "timestamp": 1525582485337
}

操作成功:返回數據

{
    "status": true,
    "result": {
        "users": [
            {"id": 1, "name": "name1"},
            {"id": 2, "name": "name2"}
        ]
    },
    "timestamp": 1525582485337
}

操作失敗:

{
    "status": false,
    "error": {
        "error_code": 5002,
        "error_reason": "illegal_argument_error",
        "error_description": "The String argument[1] must have length; it must not be null or empty"
    },
    "timestamp": 1525582485337
}
實現 定義 JSONEntity
@Data
@Accessors(chain = true)
public class JSONEntity implements Serializable {

    public enum Error {
        UNKNOWN_ERROR(-1, "unknown_error", "未知錯誤"),
        SERVER_ERROR(5000, "server_error", "服務器內部異常"),
        BUSINESS_ERROR(5001, "business_error", "業務錯誤"),
        ILLEGAL_ARGUMENT_ERROR(5002, "illegal_argument_error", "參數錯誤"),
        JSON_SERIALIZATION_ERROR(5003, "json_serialization_error", "JSON序列化失敗"),
        UNAUTHORIZED_ACCESS(5004, "unauthorized_access", "未經授權的訪問"),
        SIGN_CHECK_ERROR(5005, "sign_check_error", "簽名校驗失敗"),
        FEIGN_CALL_ERROR(5006, "feign_call_error", "遠程調用失敗");

        private int code;
        private String reason;
        private String description;

        Error(int code, String reason, String description) {
            this.code = code;
            this.reason = reason;
            this.description = description;
        }

        public int getCode() {
            return code;
        }

        public Error setCode(int code) {
            this.code = code;
            return this;
        }

        public String getReason() {
            return reason;
        }

        public Error setReason(String reason) {
            this.reason = reason;
            return this;
        }

        public String getDescription() {
            return description;
        }

        public Error setDescription(String description) {
            this.description = description;
            return this;
        }

        public static String toMarkdownTable() {
            StringBuilder stringBuilder = new StringBuilder("error_code | error_reason | error_description");
            stringBuilder.append("
:-: | :-: | :-:
");
            for (Error error : Error.values()) {
                stringBuilder.append(String.format("%s | %s | %s", error.getCode(), error.getReason(), error.getDescription())).append("
");
            }
            return stringBuilder.toString();
        }

        public static String toJsonArrayString() {
            SerializeConfig config = new SerializeConfig();
            config.configEnumAsJavaBean(Error.class);
            return JSON.toJSONString(Error.values(), config);
        }
    }

    @JSONField(ordinal = 0)
    public boolean isStatus() {
        return null == this.error;
    }

    @JSONField(ordinal = 1)
    private Result result;
    @JSONField(ordinal = 2)
    private Map error;

    @JSONField(ordinal = 3)
    public long getTimestamp() {
        return System.currentTimeMillis();
    }

    public static JSONEntity ok() {
        return JSONEntity.ok(null);
    }

    public static JSONEntity ok(Object result) {
        return new JSONEntity().setResult(result);
    }

    public static JSONEntity error() {
        return JSONEntity.error(Error.UNKNOWN_ERROR);
    }

    public static JSONEntity error(@NonNull String errorDescription) {
        return JSONEntity.error(Error.BUSINESS_ERROR, errorDescription);
    }

    public static JSONEntity error(@NonNull Throwable throwable) {
        Error error = Error.SERVER_ERROR;
        String throwMessage = throwable.getMessage();
        StackTraceElement throwStackTrace = throwable.getStackTrace()[0];
        String errorDescription = String.format("%s[%s]: %s#%s():%s", //
                null != throwMessage ? throwMessage : error.getDescription(), throwable.getClass().getTypeName(),//
                throwStackTrace.getClassName(), throwStackTrace.getMethodName(), throwStackTrace.getLineNumber()//
        );
        return JSONEntity.error(error, errorDescription);
    }

    public static JSONEntity error(@NonNull Error error) {
        return JSONEntity.error(error, error.getDescription());
    }

    public static JSONEntity error(@NonNull Error error, @NonNull String errorDescription) {
        return JSONEntity.error(error.getCode(), error.getReason(), errorDescription);
    }

    public static JSONEntity error(int errorCode, @NonNull String errorReason, @NonNull String errorDescription) {
        ImmutableMap errorMap = ImmutableMap.of(//
                "error_code", errorCode,//
                "error_reason", errorReason,//
                "error_description", errorDescription
        );
        return new JSONEntity().setError(errorMap);
    }

    @Override
    public String toString() {
        return JSON.toJSONString(this, true);
    }
    public static JSONEntity convert(@NonNull String JsonEntityJsonString) {
        return JSON.parseObject(JsonEntityJsonString, JSONEntity.class);
    }
}
定義 BusinessException
/**
 * 業務異常, 拋出后最終由 SpringMVC 攔截器統一處理為通用異常信息格式 JSON 并返回;
 */
@Data
public class AngerCloudBusinessException extends AngerCloudRuntimeException {
    private static final JSONEntity.Error UNKNOWN_ERROR = JSONEntity.Error.UNKNOWN_ERROR;
    private int errorCode = UNKNOWN_ERROR.getCode();
    private String errorReason = UNKNOWN_ERROR.getReason();
    private String errorDescription = UNKNOWN_ERROR.getDescription();

    /**
     * 服務器錯誤以及簡單的錯誤堆棧消息
     * @param cause
     */
    public AngerCloudBusinessException(Throwable cause) {
        this(JSONEntity.Error.SERVER_ERROR, String.format("%s[%s]",//
                cause.getMessage(), cause.getClass().getSimpleName()));
    }

    /**
     * 業務錯誤(附帶具體的錯誤消息)
     * @param errorDescription
     */
    public AngerCloudBusinessException(String errorDescription) {
        this(JSONEntity.Error.BUSINESS_ERROR, errorDescription);
    }

    /**
     * 指定錯誤類型
     * @param error
     */
    public AngerCloudBusinessException(JSONEntity.Error error) {
        this(error, null);
    }

    /**
     * 指定錯誤類型以及自定義消息
     * @param error
     * @param errorDescription
     */
    public AngerCloudBusinessException(JSONEntity.Error error, String errorDescription) {
        setAttributes(error);
        if (null != errorDescription) {
            this.errorDescription = errorDescription;
        }
    }

    /**
     * 未知錯誤
     */
    public AngerCloudBusinessException() {}

    /**
     * 自定義錯誤消息
     * @param errorCode
     * @param errorReason
     * @param errorDescription
     */
    public AngerCloudBusinessException(int errorCode, String errorReason, String errorDescription) {
        setAttributes(errorCode, errorReason, errorDescription);
    }

    private void setAttributes(JSONEntity.Error error) {
        this.setAttributes(error.getCode(), error.getReason(), error.getDescription());
    }

    private void setAttributes(int errorCode, String errorReason, String errorDescription) {
        this.errorCode = errorCode;
        this.errorReason = errorReason;
        this.errorDescription = errorDescription;
    }
}
@ExceptionHandler: 異常攔截處理
@Slf4j
@ResponseBody
@ControllerAdvice
public class AngerCloudHttpExceptionHandler {

    @ExceptionHandler(Exception.class)
    public ResponseEntity handlerException(Throwable throwable, HttpServletRequest request) {
        String info = String.format("HttpRequest -> , ", //
                request.getRequestURI(),//
                request.getMethod(),//
                request.getQueryString(),//
                WebUtil.getBody(request));

        // 參數錯誤: HTTP 狀態碼 400
        if (throwable instanceof IllegalArgumentException) {
            log.warn("{} handlerException-IllegalArgumentException: {}", info, ExceptionUtil.getMessage(throwable));
            return ResponseEntity.badRequest().body(JSONEntity.error(JSONEntity.Error.ILLEGAL_ARGUMENT_ERROR, throwable.getMessage()));
        }

        // 業務錯誤: HTTP 狀態碼 200
        if (throwable instanceof AngerCloudBusinessException) {
            AngerCloudBusinessException exception = (AngerCloudBusinessException) throwable;

            if (JSONEntity.Error.SERVER_ERROR.getCode() != exception.getErrorCode() //
                    && JSONEntity.Error.UNKNOWN_ERROR.getCode() != exception.getErrorCode()) {
                log.warn("{} handlerException-BusinessError: {}", info, exception.getErrorDescription());
                return ResponseEntity.ok(JSONEntity.error(exception.getErrorCode(), exception.getErrorReason(), exception.getErrorDescription()));
            }
        }

        // 系統錯誤: HTTP 狀態碼 500
        log.error(StrUtil.format("{} handlerException-Exception: {}", info, throwable.getMessage()), throwable);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(JSONEntity.error(throwable));
    }
}

關于 httpStatus 的設定,建議如下:

error error_reason http_status
illegal_argument_error 參數錯誤 400
unknown_error 未知錯誤 500
server_error 服務器內部異常 500
xxx 其他 200
使用 JSONEntity:
@GetMapping("/user/{id}")
public JSONEntity user(@PathVariable Integer id) {
    User user = find(id);
    
    if (null == user) {
        return JSONEntity.error(JSONEntity.Error.USER_NOT_FOUND);
    }
    return JSONEntity.ok(ImmutableMap.of("user": user));
}

-->
{
    "status": true,
    "result": {
        "user": {"id": 1, "name": "user1"}
    },
    "timestamp": 1525582485337
}

{
    "status": false,
    "error": {
        "error_code": 10086,
        "error_reason": "user_not_found",
        "error_description": "沒有找到用戶 #user1"
    },
    "timestamp": 1525582485337
}
Assert: 參數檢查
Assert.noEmpty(name, "用戶名不能為空");

// 或者手動拋出IllegalArgumentException異常
if (StringUtil.isEmpty(name)) {
    throw new IllegalArgumentException("用戶名不能為空");
}

-->
{
    "status": false,
    "error": {
        "error_code": 5002,
        "error_reason": "illegal_argument_error",
        "error_description": "用戶名不能為空"
    },
    "timestamp": 1525582485337
}
Exception: 拋出業務異常
// 使用系統定義的錯誤
throw new AngerCloudBusinessException(JSONEntity.Error.USER_NOT_FOUND)

// 指定系統定義的錯誤, 錯誤消息使用異常信息中攜帶的消息
throw new AngerCloudBusinessException(JSONEntity.Error.USER_NOT_FOUND, ex);

// 指定系統定義的錯誤, 但指定了新的錯誤消息.
throw new AngerCloudBusinessException(JSONEntity.Error.USER_NOT_FOUND, "用戶 XXX 沒有找到");

// 手動拋出服務器異常 (SERVER_ERROR)
throw new AngerCloudBusinessException(ex);

// 拋出一個未知的異常 (UNKNOWN_ERROR)
throw new AngerCloudBusinessException();

// 自定義錯誤消息
thow new AngerCloudBusinessException(10086, "user_email_exists", "用戶郵箱已經存在了");

{
    "status": false,
    "error": {
        "error_code": 10086,
        "error_reason": "user_email_exists",
        "error_description": "用戶郵箱已經存在了"
    },
    "timestamp": 1525582485337
}
補充:提供可維護的 ErrorCode 列表
@GetMapping("/error_code")
public JSONEntity errors() {
    return JSONEntity.ok(ImmutableMap.of(//
        "enums", JSONEntity.Error.values(),//
        "markdownText", JSONEntity.Error.toMarkdownTable(), //
        "jsonString", JSONEntity.Error.toJsonArrayString()));
}

最終,該接口會根據系統定義(JSONEntity.Error)的異常信息,返回 Markdown 文本或 JSON 字符串,前端解析后生成表格即可,就像下面這樣:

error_code error_reason error_description
-1 unknown_error 未知錯誤
5000 server_error 服務器內部異常
5001 illegal_argument_error 參數錯誤
5002 json_serialization_error JSON 序列化失敗
... ... ...

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71521.html

相關文章

  • Spring Boot 2.x(十):構建優雅RESTful接口

    摘要:滿足這些約束條件和原則的應用程序或設計就是。需要注意的是,是設計風格而不是標準。同一個路徑,因為請求方式的不同,而去找尋不同的接口,完成對資源狀態的轉變。一個符合風格的就可以稱之一個的接口。 RESTful 相信在座的各位對于RESTful都是略有耳聞,那么RESTful到底是什么呢? REST(Representational State Transfer)表述性狀態轉移是一組架構約...

    nevermind 評論0 收藏0
  • SpringBoot非官方教程 | 第十一篇:SpringBoot集成swagger2,構建優雅R

    摘要:另外很容易構建風格的,簡單優雅帥氣,正如它的名字。配置一些基本的信息。三寫生產文檔的注解通過注解表明該接口會生成文檔,包括接口名請求方法參數返回信息的等等。四參考資料中使用構建強大的文檔 swagger,中文拽的意思。它是一個功能強大的api框架,它的集成非常簡單,不僅提供了在線文檔的查閱,而且還提供了在線文檔的測試。另外swagger很容易構建restful風格的api,簡單優雅帥氣...

    荊兆峰 評論0 收藏0
  • 后端技術精選

    摘要:服務教程在它提出十多年后的今天,已經成為最重要的應用技術之一。全方位提升網站打開速度前端后端新的技術如何在內完整打開網站會直接影響用戶的滿意度及留存率,在前端后端數據緩存加速等等方面都有諸多可以提升。 HTTPS 原理剖析與項目場景 最近手頭有兩個項目,XX 導航和 XX 產業平臺,都需要使用 HTTPS 協議,因此,這次對 HTTPS 協議做一次整理與分享。 使用緩存應該注意哪些問題...

    GitCafe 評論0 收藏0
  • 后端技術精選

    摘要:服務教程在它提出十多年后的今天,已經成為最重要的應用技術之一。全方位提升網站打開速度前端后端新的技術如何在內完整打開網站會直接影響用戶的滿意度及留存率,在前端后端數據緩存加速等等方面都有諸多可以提升。 HTTPS 原理剖析與項目場景 最近手頭有兩個項目,XX 導航和 XX 產業平臺,都需要使用 HTTPS 協議,因此,這次對 HTTPS 協議做一次整理與分享。 使用緩存應該注意哪些問題...

    explorer_ddf 評論0 收藏0
  • 后端技術精選

    摘要:服務教程在它提出十多年后的今天,已經成為最重要的應用技術之一。全方位提升網站打開速度前端后端新的技術如何在內完整打開網站會直接影響用戶的滿意度及留存率,在前端后端數據緩存加速等等方面都有諸多可以提升。 HTTPS 原理剖析與項目場景 最近手頭有兩個項目,XX 導航和 XX 產業平臺,都需要使用 HTTPS 協議,因此,這次對 HTTPS 協議做一次整理與分享。 使用緩存應該注意哪些問題...

    Jensen 評論0 收藏0

發表評論

0條評論

MingjunYang

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<