【问题标题】:How to produce API error responses in Laravel 5.4?如何在 Laravel 5.4 中生成 API 错误响应?
【发布时间】:2017-04-06 04:43:12
【问题描述】:

每当我呼叫/api/v1/posts/1 时,呼叫都会转接到show 方法

public function show(Post $post) {
    return $post;
}

PostController.php 资源丰富的控制器中。如果帖子确实存在,则服务器返回 JSON 响应。但是,如果帖子确实存在,服务器会返回纯 HTML,尽管请求显然期望返回 JSON。这是 Postman 的演示。

问题在于 API 应该返回 application/json,而不是 text/html。所以,这是我的问题:

1. 如果我们使用隐式路由模型绑定时发生异常,Laravel 是否内置支持自动返回 JSON(如上面的 show 方法中,当我们有 404)?

2. 如果可以,我该如何启用它? (默认情况下,我得到的是纯 HTML,而不是 JSON)

如果不是在每个 API 控制器中复制以下内容的替代方法

public function show($id) {
    $post = Post::find($id); // findOrFail() won't return JSON, only plain HTML
    if (!$post)
        return response()->json([ ... ], 404);
    return $post;
}

3.app\Exceptions\Handler 中是否有通用方法可以使用?

4.标准错误/异常响应包含什么?我用谷歌搜索了这个,但发现了许多自定义变体。

5. 为什么 JSON 响应仍未内置到隐式路由模型绑定中?为什么不简化开发人员的生活并自动处理这些低级问题?

编辑

在 Laravel IRC 的人建议我不要理会错误响应之后,我遇到了一个难题,他们认为 标准 HTTP 异常默认呈现为 HTML,以及使用 API 的系统应该在不看身体的情况下处理 404。我希望更多的人加入讨论,不知道你们会如何回应。

【问题讨论】:

    标签: laravel laravel-5.4


    【解决方案1】:

    我在app/Exceptions/Handler.php 中使用此代码,您可能需要进行一些更改

    public function render($request, Exception $exception)
    {
        $exception = $this->prepareException($exception);
    
        if ($exception instanceof \Illuminate\Http\Exception\HttpResponseException) {
            return $exception->getResponse();
        }
        if ($exception instanceof \Illuminate\Auth\AuthenticationException) {
            return $this->unauthenticated($request, $exception);
        }
        if ($exception instanceof \Illuminate\Validation\ValidationException) {
            return $this->convertValidationExceptionToResponse($exception, $request);
        }
    
        $response = [];
    
        $statusCode = 500;
        if (method_exists($exception, 'getStatusCode')) {
            $statusCode = $exception->getStatusCode();
        }
    
        switch ($statusCode) {
            case 404:
                $response['error'] = 'Not Found';
                break;
    
            case 403:
                $response['error'] = 'Forbidden';
                break;
    
            default:
                $response['error'] = $exception->getMessage();
                break;
        }
    
        if (config('app.debug')) {
            $response['trace'] = $exception->getTrace();
            $response['code'] = $exception->getCode();
        }
    
        return response()->json($response, $statusCode);
    }
    

    另外,如果你要使用formRequest验证,你需要重写方法response,否则你会被重定向,可能会导致一些错误。

    use Illuminate\Http\JsonResponse;
    
    ...
    
    public function response(array $errors)
    {
        // This will always return JSON object error messages
        return new JsonResponse($errors, 422);
    }
    

    【讨论】:

    • @DDurham 已经完成,这是 L5.4 的完整示例,prepareException 是来自Illuminate\Foudantion\Exceptions\Hanlder 的方法laravel.com/api/5.4/Illuminate/Foundation/Exceptions/… 请在发表此类评论之前查看您的 cmets,并确保你不会评论任何错误。这对任何人都没有帮助。
    • response 函数去哪儿了?我进行了全局搜索,但找不到具有该签名的方法。
    • @JonMcClung response 是 Laravel 助手
    • 好的,你的意思是按照here的步骤操作?
    • @JulianoPetronetto,这个解决方案帮助了我!
    【解决方案2】:
    1. 是否有在 app\Exceptions\Handler 中使用的通用方法?

    您可以检查通用异常处理程序中是否需要 json。

    // app/Exceptions/Handler.php
    public function render($request, Exception $exception) {
        if ($request->expectsJson()) {
            return response()->json(["message" => $exception->getMessage()]);
        }
        return parent::render($request, $exception);
    }
    

    【讨论】:

    • 这似乎有问题,因为您必须指定响应代码(在这种情况下应该是 404,而不是 200)。实际上,您必须至少对所有主要例外情况都这样做......
    【解决方案3】:

    我们通过创建一个处理返回响应部分的基本控制器来处理它的方式。看起来像这样,

    class BaseApiController extends Controller
    {
    
        private $responseStatus = [
            'status' => [
                'isSuccess' => true,
                'statusCode' => 200,
                'message' => '',
            ]
        ];
    
        // Setter method for the response status
        public function setResponseStatus(bool $isSuccess = true, int $statusCode = 200, string $message = '')
        {
            $this->responseStatus['status']['isSuccess'] = $isSuccess;
            $this->responseStatus['status']['statusCode'] = $statusCode;
            $this->responseStatus['status']['message'] = $message;
        }
    
        // Returns the response with only status key
        public function sendResponseStatus($isSuccess = true, $statusCode = 200, $message = '')
        {
    
            $this->responseStatus['status']['isSuccess'] = $isSuccess;
            $this->responseStatus['status']['statusCode'] = $statusCode;
            $this->responseStatus['status']['message'] = $message;
    
            $json = $this->responseStatus;
    
            return response()->json($json, $this->responseStatus['status']['statusCode']);
    
        }
    
        // If you have additional data to send in the response
        public function sendResponseData($data)
        {
    
            $tdata = $this->dataTransformer($data);
    
            if(!empty($this->meta)) $tdata['meta'] = $this->meta;
    
            $json = [
                'status' => $this->responseStatus['status'],
                'data' => $tdata,
            ];
    
    
            return response()->json($json, $this->responseStatus['status']['statusCode']);
    
        }
    }
    

    现在你需要在你的控制器中扩展它

    class PostController extends BaseApiController {
    
        public function show($id) {
            $post = \App\Post::find($id);
            if(!$post) {
                return $this->sendResponseStatus(false, 404, 'Post not found');
            }
    
            $this->setResponseStatus(true, 200, 'Your post');
            return $this->sendResponseData(['post' => $post]);
        }
    }
    

    你会得到这样的回应

    {
      "status": {
        "isSuccess": false,
        "statusCode": 404,
        "message": "Post not found"
      }
    }
    
    {
      "status": {
        "isSuccess": true,
        "statusCode": 200,
        "message": "Your post"
      },
      "data": {
         "post": {
             //Your post data
          }
      }
    }
    

    【讨论】:

    • 当框架已经完成了大部分工作时,这有点矫枉过正,而且很多代码只是为了处理 API 异常
    【解决方案4】:

    您只需使用use Illuminate\Support\Facades\Response;。 然后,将返回作为 am:

    public function index(){
        $analysis = Analysis::all();
        if(empty($analysis)) return Response::json(['error'=>'Empty data'], 200);
        return Response::json($analysis, 200, [], JSON_NUMERIC_CHECK);
    }
    

    现在你将得到一个 JSON 返回......

    【讨论】:

      猜你喜欢
      • 2019-04-14
      • 2017-10-11
      • 2019-12-31
      • 2018-01-14
      • 2017-08-08
      • 1970-01-01
      • 2020-03-06
      • 2015-06-06
      • 1970-01-01
      相关资源
      最近更新 更多