Laravel API,如何正确处理错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/51065170/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 17:50:59  来源:igfitidea点击:

Laravel API, how to properly handle errors

laravelapierror-handling

提问by Jessy

Anyone know what is the best way to handle errors in Laravel, there is any rules or something to follow ?

任何人都知道在 Laravel 中处理错误的最佳方法是什么,有什么规则或要遵循的东西吗?

Currently i'm doing this :

目前我正在这样做:

public function store(Request $request)
{
  $plate = Plate::create($request->all());

  if ($plate) {
    return $this->response($this->plateTransformer->transform($plate));
  } else {
    // Error handling ?
    // Error 400 bad request
    $this->setStatusCode(400);
    return $this->responseWithError("Store failed.");
  }
}

And the setStatusCode and responseWithError come from the father of my controller :

而 setStatusCode 和 responseWithError 来自我的控制器的父亲:

public function setStatusCode($statusCode)
{
    $this->statusCode = $statusCode;

    return $this;
}

public function responseWithError ($message )
{
    return $this->response([
        'error' => [
            'message' => $message,
            'status_code' => $this->getStatusCode()
        ]
    ]);

}

But is this a good way to handle the API errors, i see some different way to handle errors on the web, what is the best ?

但这是处理 API 错误的好方法吗?我看到了一些不同的处理网络错误的方法,最好的方法是什么?

Thanks.

谢谢。

回答by rkj

Try this, i have used it in my project(app/Exceptions/Handler.php)

试试这个,我在我的项目中使用过它(app/Exceptions/Handler.php)

public function render($request, Exception $exception)
{
    if ($request->wantsJson()) {   //add Accept: application/json in request
        return $this->handleApiException($request, $exception);
    } else {
        $retval = parent::render($request, $exception);
    }

    return $retval;
}

Now Handle Api exception

现在处理 Api 异常

private function handleApiException($request, Exception $exception)
{
    $exception = $this->prepareException($exception);

    if ($exception instanceof \Illuminate\Http\Exception\HttpResponseException) {
        $exception = $exception->getResponse();
    }

    if ($exception instanceof \Illuminate\Auth\AuthenticationException) {
        $exception = $this->unauthenticated($request, $exception);
    }

    if ($exception instanceof \Illuminate\Validation\ValidationException) {
        $exception = $this->convertValidationExceptionToResponse($exception, $request);
    }

    return $this->customApiResponse($exception);
}

After that custom Api handler response

在自定义 Api 处理程序响应之后

private function customApiResponse($exception)
{
    if (method_exists($exception, 'getStatusCode')) {
        $statusCode = $exception->getStatusCode();
    } else {
        $statusCode = 500;
    }

    $response = [];

    switch ($statusCode) {
        case 401:
            $response['message'] = 'Unauthorized';
            break;
        case 403:
            $response['message'] = 'Forbidden';
            break;
        case 404:
            $response['message'] = 'Not Found';
            break;
        case 405:
            $response['message'] = 'Method Not Allowed';
            break;
        case 422:
            $response['message'] = $exception->original['message'];
            $response['errors'] = $exception->original['errors'];
            break;
        default:
            $response['message'] = ($statusCode == 500) ? 'Whoops, looks like something went wrong' : $exception->getMessage();
            break;
    }

    if (config('app.debug')) {
        $response['trace'] = $exception->getTrace();
        $response['code'] = $exception->getCode();
    }

    $response['status'] = $statusCode;

    return response()->json($response, $statusCode);
}

Always add Accept: application/jsonin your api or json request.

始终添加Accept: application/json您的 api 或 json 请求。

回答by Andrea Mauro

Laravel is already able to manage json responses by default.

默认情况下,Laravel 已经能够管理 json 响应。

Withouth customizing the render method in app\Handler.php you can simply throw a Symfony\Component\HttpKernel\Exception\HttpException, the default handler will recognize if the request header contains Accept: application/jsonand will print a json error message accordingly.

无需在 app\Handler.php 中自定义渲染方法,您只需抛出一个 Symfony\Component\HttpKernel\Exception\HttpException,默认处理程序将识别请求头是否包含Accept: application/json并相应地打印 json 错误消息。

If debug mode is enabled it will output the stacktrace in json format too.

如果启用调试模式,它也会以 json 格式输出堆栈跟踪。

Here is a quick example:

这是一个快速示例:

<?php

...

use Symfony\Component\HttpKernel\Exception\HttpException;

class ApiController
{
    public function myAction(Request $request)
    {
        try {
            // My code...
        } catch (\Exception $e) {
            throw new HttpException(500, $e->getMessage());
        }

        return $myObject;
    }
}

Here is laravel response with debug off

这是关闭调试的 Laravel 响应

{
    "message": "My custom error"
}

And here is the response with debug on

这是带有调试的响应

{
    "message": "My custom error",
    "exception": "Symfony\Component\HttpKernel\Exception\HttpException",
    "file": "D:\www\myproject\app\Http\Controllers\ApiController.php",
    "line": 24,
    "trace": [
        {
            "file": "D:\www\myproject\vendor\laravel\framework\src\Illuminate\Routing\ControllerDispatcher.php",
            "line": 48,
            "function": "myAction",
            "class": "App\Http\Controllers\ApiController",
            "type": "->"
        },
        {
            "file": "D:\www\myproject\vendor\laravel\framework\src\Illuminate\Routing\Route.php",
            "line": 212,
            "function": "dispatch",
            "class": "Illuminate\Routing\ControllerDispatcher",
            "type": "->"
        },

        ...
    ]
}

Using HttpException the call will return the http status code of your choice (in this case internal server error 500)

使用 HttpException 调用将返回您选择的 http 状态代码(在这种情况下内部服务器错误 500)

回答by user3574492

In my opinion I'd keep it simple.

在我看来,我会保持简单。

Return a response with the HTTP error code and a custom message.

返回带有 HTTP 错误代码和自定义消息的响应。

return response()->json(['error' => 'You need to add a card first'], 500);

Or if you want to throw a caught error you could do :

或者,如果你想抛出一个捕获的错误,你可以这样做:

   try {
     // some code
    } catch (Exception $e) {
        return response()->json(['error' => $e->getMessage()], 500);
    }

You can even use this for sending successful responses:

您甚至可以使用它来发送成功的响应:

return response()->json(['activeSubscription' => $this->getActiveSubscription()], 200);

This way no matter which service consumes your API it can expect to receive the same responses for the same requests.

这样,无论哪个服务使用您的 API,它都可以期望收到相同请求的相同响应。

You can also see how flexible you can make it by passing in the HTTP status code.

您还可以通过传入 HTTP 状态代码来了解它的灵活性。

回答by Андрей Русев

I think it would be better to modify existing behaviour implemented in app/Exceptions/Handler.phpthan overriding it.

我认为修改app/Exceptions/Handler.php 中实现的现有行为比覆盖它更好

You can modify JSONResponse returned by parent::render($request, $exception);and add/remove data.

您可以修改返回的 JSONResponseparent::render($request, $exception);并添加/删除数据。

Example implementation:
app/Exceptions/Handler.php

示例实现:
app/Exceptions/Handler.php

use Illuminate\Support\Arr;

// ... existing code

public function render($request, Exception $exception)
{
    if ($request->is('api/*')) {
        $jsonResponse = parent::render($request, $exception);
        return $this->processApiException($jsonResponse);
    }

    return parent::render($request, $exception);
}

protected function processApiException($originalResponse)
{
    if($originalResponse instanceof JsonResponse){
        $data = $originalResponse->getData(true);
        $data['status'] = $originalResponse->getStatusCode();
        $data['errors'] = [Arr::get($data, 'exception', 'Something went wrong!')];
        $data['message'] = Arr::get($data, 'message', '');
        $originalResponse->setData($data);
    }

    return $originalResponse;
}