Laravel API,如何正确处理错误

Laravel API, how to properly handle errors

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

目前我正在这样做:

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.");
  }
}

而 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()
        ]
    ]);

}

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

谢谢。

试试这个,我已经在我的项目中使用了它 (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;
}

现在处理 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);
}

在自定义 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);
}

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

我认为我会保持简单。

Return 包含 HTTP 错误代码和自定义消息的响应。

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

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

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

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

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

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

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

默认情况下,

Laravel 已经能够管理 json 个回复。

在app\Handler.php中不自定义渲染方法,你可以简单地抛出一个Symfony\Component\HttpKernel\Exception\HttpException,如果请求头包含Accept,默认处理程序将识别: application/json 并将相应地打印 json 错误消息。

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

这是一个简单的例子:

<?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;
    }
}

这是 laravel 关闭调试的响应

{
    "message": "My custom error"
}

这是启用调试的响应

{
    "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": "->"
        },

        ...
    ]
}

使用 HttpException 调用将 return 您选择的 http 状态代码(在本例中为内部服务器错误 500)

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

您可以修改parent::render($request, $exception);和add/remove数据返回的JSONResponse。

示例实现:
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;
}

使用@RKJ 最佳答案中的一些代码,我以这种方式处理了错误:

打开 "Illuminate\Foundation\Exceptions\Handler" class 并搜索名为 "convertExceptionToArray" 的方法。此方法将 HTTP 异常转换为数组以显示为响应。在这个方法中,我只是调整了一小段代码,不会影响松耦合。

所以用这个

替换convertExceptionToArray方法
protected function convertExceptionToArray(Exception $e, $response=false)
    {

        return config('app.debug') ? [
            'message' => $e->getMessage(),
            'exception' => get_class($e),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'trace' => collect($e->getTrace())->map(function ($trace) {
                return Arr::except($trace, ['args']);
            })->all(),
        ] : [
            'message' => $this->isHttpException($e) ? ($response ? $response['message']: $e->getMessage()) : 'Server Error',
        ];
    }

现在导航到 App\Exceptions\Handler class 并将以下代码粘贴到 render 方法上方:

public function convertExceptionToArray(Exception $e, $response=false){

        if(!config('app.debug')){
            $statusCode=$e->getStatusCode();
            switch ($statusCode) {
            case 401:
                $response['message'] = 'Unauthorized';
                break;
            case 403:
                $response['message'] = 'Forbidden';
                break;
            case 404:
                $response['message'] = 'Resource Not Found';
                break;
            case 405:
                $response['message'] = 'Method Not Allowed';
                break;
            case 422:
                $response['message'] = 'Request unable to be processed';
                break;
            default:
                $response['message'] = ($statusCode == 500) ? 'Whoops, looks like something went wrong' : $e->getMessage();
                break;
            }
        }

        return parent::convertExceptionToArray($e,$response);
    }

基本上,我们覆盖了 convertExceptionToArray 方法,准备了响应消息,并通过将响应作为参数传递来调用父方法。

注意:此解决方案不适用于 Authentication/Validation 错误,但大多数情况下,Laravel 通过适当的 [=] 可以很好地管理这两个错误43=] 回复消息。

在你的 handler.php 这应该可以处理 404 异常。

public function render($request, Throwable $exception ){
    if ($exception instanceof ModelNotFoundException) {
        return response()->json([
            'error' => 'Data not found'
        ], 404);
    }
    return parent::render($request, $exception);
}

对我来说,最好的方法是对 API 响应使用特定的异常。

如果您使用 Laravel 版本 > 5.5,您可以 create your own exception 使用 report()render() 方法。使用命令: php artisan make:exception AjaxResponseException

它将在以下位置创建 AjaxResponseException.php: app/Exceptions/
之后用你的逻辑填充它。例如:

/**
 * Report the exception.
 *
 * @return void
 */
public function report()
{
    \Debugbar::log($this->message);
}

/**
 * Render the exception into an HTTP response.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return JsonResponse|Response
 */
public function render($request)
{
    return response()->json(['error' => $this->message], $this->code);
}

现在,您可以在具有 try/catch 功能的 ...Controller 中使用它。
例如你的方式:

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

        if ($plate) {
            return $this->response($this->plateTransformer->transform($plate));
        }

        throw new AjaxResponseException("Plate wasn't created!", 404);

    }catch (AjaxResponseException $e) {
        throw new AjaxResponseException($e->getMessage(), $e->getCode());
    }
}

这足以使您的代码更易于阅读、漂亮且有用。
此致!

好吧,现在所有答案都可以,但他们也在使用旧方法。 在 Laravel 8 之后,您可以通过将异常 class 引入为 renderable:

来简单地更改 register() 方法中的响应
<?php


namespace Your\Namespace;


use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;


class Handler extends ExceptionHandler
{
    /**
     * Register the exception handling callbacks for the application.
     *
     * @return void
     */
    public function register()
    {
        $this->renderable(function (NotFoundHttpException $e, $request) {
            if ($request->is('api/*')) {
                return response()->json([
                    'message' => 'Record not found.'
                ], 404);
            }
        });
    }
}