Laravel: Стандартные ответы API с Responsable классами

Источник: «Standard API Responses With Laravel Responsables»
Представьте работу приложения, в котором каждая конечная точка по-разному возвращает данные об успешных вызовах и вызовах с ошибками. Если это маленькое и простое приложение, поддерживаемое одним человеком, это может быть немного проще. Но если приложение начнёт расти и к команде присоединится больше людей, через некоторое время будет полный хаос для поддержки и улучшения приложения.

Наличие стандартизированной структуры ответов приложения может сильно помочь в улучшении его качества и удобства сопровождения. В этой статье мы узнаем простой способ сделать это с помощью Laravel Responsable классов.

Что такое Laravel Responsable классы?

В Laravel есть интерфейс Illuminate\Contracts\Support\Responsable, который можно использовать для создания классов, конвертируемых в HTTP-ответы (HTTP-Responses). Это действительно простой интерфейс с одним методом:

interface Responsable
{
/**
* Create an HTTP response that represents the object.
*
* @param \Illuminate\Http\Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/

public function toResponse($request);
}

Создание пользовательских классов ответов

Создание пользовательских Response классов даёт много возможностей, но нужно быть осторожным в их использовании. Я уже видел много кодовых баз и статей, объясняющих использование пользовательских Response классов для форматирования данных и применения некоторой логики к данным перед их отправкой. Но я не думаю, что это хороший способ использования этой функции. Для меня форматирование кода и логика должны быть на другом уровне приложения.

Но одна удивительная вещь, которую мы можем сделать с помощью этой функции, — создавать стандартизированные ответы для наших приложений. Наличие этого в приложениях может значительно улучшить их качество и удобство сопровождения, потому что вы уже знаете структуру, которую ожидаете от приложения.

В следующих разделах мы создадим два разных настраиваемых класса ответов: один для успешных ответов API, а другой — для неудачных ответов API.

Создание успешного API ответа

Представьте, что в вашем приложении конечные точки всегда возвращают необходимые для него данные, но также могут возвращать некоторые мета данные, которые могут содержать сообщения или уведомления для отображения пользователю информации о разбиении на страницы и т.д. Имея это ввиду, мы можем создать простой класс Response, возвращающий два свойства в ответах: данные и метаданные. Пользовательский Response класс будет выглядеть примерно так:

namespace App\Http\Responses;

use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\Response;

class ApiSuccessResponse implements Responsable
{
/**
* @param mixed $data
* @param array $metadata
* @param int $code
* @param array $headers
*/

public function __construct(
private mixed $data,
private array $metadata,
private int $code = Response::HTTP_OK,
private array $headers = []
) {}

/**
* @param $request
* @return \Symfony\Component\HttpFoundation\Response|void
*/

public function toResponse($request)
{
return response()->json(
[
'data' => $this->data,
'metadata' => $this->metadata,
],
$this->code,
$this->headers
);
}
}

С помощью этого касса мы можем гарантировать, что:

  1. Ответ всегда будет в формате JSON.
  2. Мы всегда будем возвращать свойства данных и метаданных.
  3. При необходимости мы можем настроить HTTP-код и заголовок.

Теперь вы можете использовать этот Response класс для своих конечных точек.

class UserController extends Controller
{
public function store(CreateUserRequest $request): JsonResponse
{
$user = $this->service->create($request->all());
return new ApiSuccessResponse(
$user,
['message' => 'User was created successfully'],
Response::HTTP_CREATED
);
}
}

Создание API ответа об ошибке

Представьте, что в вашем приложении вы хотите всегда возвращать сообщение об ошибке, когда что-то идёт не так, но, вы также хотите добавить некую отладочную информацию, если debug = true. Имея это ввиду мы можем создать ещё один простой Response класс, который сделает это:

namespace App\Http\Responses;

use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\Response;
use Throwable;

class ApiErrorResponse implements Responsable
{
public function __construct(
private string $message,
private ?Throwable $exception = null,
private int $code = Response::HTTP_INTERNAL_SERVER_ERROR,
private array $headers = []
) {}

/**
* @param $request
* @return \Symfony\Component\HttpFoundation\Response|void
*/

public function toResponse($request)
{
$response = ['message' => $this->message];

if (! is_null($this->exception) && config('app.debug')) {
$response['debug'] = [
'message' => $this->exception->getMessage(),
'file' => $this->exception->getFile(),
'line' => $this->exception->getLine(),
'trace' => $this->exception->getTraceAsString()
];
}

return response()->json($response, $this->code, $this->headers);
}
}

С помощью этого класса мы можем гарантировать, что:

  1. Ответ всегда будет в формате JSON.
  2. Мы всегда будем возвращать свойство message.
  3. При необходимости будет добавлена отладочная информация.
  4. При необходимости мы можем настроить HTTP-код и заголовки.

Теперь вы можете использовать этот Response класс для своих конечных точек:

class UserController extends Controller
{
public function store(CreateUserRequest $request): JsonResponse
{
try {
$user = $this->service->create($request->all());
return new ApiSuccessResponse(
$user,
['message' => 'User was created successfully'],
Response::HTTP_CREATED
);
} catch (Throwable $exception) {
return new ApiErrorResponse(
'An error occurred while trying to create the user',
$exception
);
}
}
}

Заключение

В этой статье мы узнали, как использовать Laravel Responsable классы для создания стандартизированных API ответов для своих приложений, для улучшения их качества и удобства сопровождения. Примеры в этой статье были просто для того, чтобы показать, как использовать эту функцию, но вы можете настроить пользовательские Response классы для нужд своих проектов.

Надеюсь вам понравилась эта статья, и если да, то не забудьте поделиться ею с друзьями и коллегами! До встречи!

Дополнительные материалы

Предыдущая Статья

PHP 8.3: gc_status дополнительная информацию о GC

Следующая Статья

JavaScript: Новое событие scrollend