規範 API 介面錯誤響應格式

天衣有縫 發表於 2021-07-08

廢話不多說了,直接上程式碼。

定義 json 返回資料格式

首先定義一個 trait

app/Responses/ResponseJson.php

<?php
namespace App\Http\Responses;

trait ResponseJson
{
    private function jsonResponse($status, $code, $message, $data, $error)
    {
        $result = [
            'status'  => $status,
            'code'    => $code,
            'message' => $message,
            'data'    => $data,
            'error'  => $error,
        ];
        return response()->json($result);
    }

    public function jsonSuccessData($data)
    {
        return $this->jsonResponse('success', 200, '請求成功', $data, []);
    }

    public function jsonErrorsData($code, $message, $data = [])
    {
        return $this->jsonResponse('fail', $code, '請求失敗', $data, $message);
    }
}

統一的資料響應格式,固定包含:codestatusdatamessageerror

  • 成功直接呼叫 jsonSuccessData方法,傳入$data資料;
  • 失敗呼叫 jsonErrorsData 方法,傳入 $code$message$data, $data可以為空;

在基類控制器中引用

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Responses\ResponseJson;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests, ResponseJson;
}

修改 Handler 函式

App\Exceptions\Handler.php

<?php

namespace App\Exceptions;

use Throwable;

use App\Http\Responses\ResponseJson;

use Illuminate\Database\QueryException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Validation\ValidationException;
use \Symfony\Component\HttpKernel\Exception\HttpException;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

class Handler extends ExceptionHandler
{
    use ResponseJson;

    /**
     * A list of the exception types that are not reported.
     *
     * @var array
     */
    protected $dontReport = [
        //
    ];

    /**
     * A list of the inputs that are never flashed for validation exceptions.
     *
     * @var array
     */
    protected $dontFlash = [
        'current_password',
        'password',
        'password_confirmation',
    ];

    /**
     * Register the exception handling callbacks for the application.
     *
     * @return void
     */
    public function register()
    {
        $this->reportable(function (Throwable $e) {
            //
        });
    }

    public function render($request, Throwable $e)
    {
        $data = [];

        // 客戶端錯誤
        if ($e instanceof ApiException) {

            $code = $e->getCode();
            $message = $e->getMessage();

        } else if ($e instanceof QueryException) {
            // 資料庫錯誤
            $code = 500;
            $message = '資料庫錯誤,請聯絡開發者';
            if (env('APP_DEBUG')) {
                $data = $e->getMessage();
            }

        }  else if ($e instanceof ValidationException) {
            // 驗證錯誤
            $code = 422;
            $message = '引數格式錯誤';
            $data = $e->errors();

        }  else if ($e instanceof HttpException) {
            // URL不正確
            $code = 404;
            $message = '未定義路由,請檢查 URL 是否正確';

        }  else if ($e instanceof AuthenticationException) {
            // token異常
            $code = 403;
            $message = 'token異常,禁止訪問';

        } else {
            // 其他異常
            $code = 500;
            $message = '伺服器異常';
            $data = $e->getMessage();
        }

        return $this->jsonErrorsData($code, $message, $data);
    }
}

程式碼說明:

  • 首先 use 幾個檔案:

    • use Illuminate\Database\QueryException;

      這是用來捕獲資料錯誤的,例如:資料庫連線失敗,查詢欄位不存在等等;

    • use Illuminate\Auth\AuthenticationException;

      這是用來捕獲身份驗證錯誤的,例如:token過期;

    • use Illuminate\Validation\ValidationException;

      這是用來捕獲表單驗證錯誤的,例如:引數格式不對、引數缺失等等;

    • use \Symfony\Component\HttpKernel\Exception\HttpException;

      這是用來捕獲 http 請求異常的,例如:url不存在

      備註: 還有一些其他類用來捕獲其他型別的異常,請自行查詢,以上只列出常用的幾種異常捕獲

  • rander函式中,用instanceof方法將$e判斷出是哪種錯誤,然後定義該種錯誤的狀態碼,獲取到錯誤資訊,然後返回給jsonErrorsData函式;

    測試

  • 獲取資料列表介面:

    • 成功:
      {
        "status": "success",
        "code": 200,
        "message": "請求成功",
        "data": {
            "data": [
                {
                    "machine": "裝置3",
                    "device": "油、氣、水路系統維保單元",
                    "grade": "一級保養專案",
                    "cycle": "40小時",
                    "use": "70小時24分鐘",
                },
                ......
            ],
            "links": {
                "first": "http://mes.app/api/v1/maintains/list?page=1",
                "last": "http://mes.app/api/v1/maintains/list?page=2",
                "prev": null,
                "next": "http://mes.app/api/v1/maintains/list?page=2"
            },
            "meta": {
                "current_page": 1,
                "from": 1,
                "last_page": 2,
                "links": [
                    {
                        "url": null,
                        "label": "&laquo; 上一頁",
                        "active": false
                    },
                    {
                        "url": "http://mes.app/api/v1/maintains/list?page=1",
                        "label": "1",
                        "active": true
                    },
                    {
                        "url": "http://mes.app/api/v1/maintains/list?page=2",
                        "label": "2",
                        "active": false
                    },
                    {
                        "url": "http://mes.app/api/v1/maintains/list?page=2",
                        "label": "下一頁 &raquo;",
                        "active": false
                    }
                ],
                "path": "",
                "per_page": 10,
                "to": 10,
                "total": 15
            }
        },
        "error": []
      }
    • 失敗:
      {
        "status": "fail",
        "code": 422,
        "message": "請求失敗",
        "data": {
            "machine": [
                "裝置 只能由字母、數字、短劃線(-)和下劃線(_)組成。",
                "裝置 不能為空。"
            ]
        },
        "error": "引數格式錯誤"
      }
本作品採用《CC 協議》,轉載必須註明作者和本文連結