Стандартные ответы API с использованием интерфейса Responsable
Введение
Представьте себе, что вы работаете с приложением, в котором каждая конечная точка возвращает данные об успешных и ошибочных вызовах по-разному. Если это маленькое и простое приложение, поддерживаемое одним человеком, это может быть немного проще, но если приложение начнет расти и к команде начнет присоединяться все больше людей, то за небольшой промежуток времени поддержка и улучшение приложения станет полным хаосом. приложение.
Наличие стандартизированной структуры ответов вашего приложения может очень помочь в повышении его качества и удобства сопровождения. В этой статье мы узнаем простой способ сделать это с помощью классов Laravel Responsable.
Что такое ответственные классы Laravel?
У Laravel есть интерфейс: Illuminate\Contracts\Support\Responsable его можно использовать для
создания классов, которые можно преобразовать в HTTP-ответы. Это действительно простой
интерфейс, имеющий только один метод:
namespace Illuminate\Contracts\Support;
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, который всегда будет возвращать два свойства в ваших ответах: data и metadata.
Пользовательский класс 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
);
}
}
С помощью этого класса мы можем гарантировать, что:
- Ответ всегда будет в формате JSON.
- Мы всегда будем возвращать свойства data и metadata.
- При необходимости мы можем настроить HTTP-код и заголовки.
Теперь вы можете использовать этот ответ для своих конечных точек
class UserController extends Controller
{
public function store(CreateUserRequest $request): JsonResponse
{
$user = $this->service->create($request->all());
return new ApiSuccessResponse(
['message' => 'User was created successfully'],
Response::HTTP_CREATED
);
}
}
Можно доработать класс, добавив статический метод создания экземпляра класса и добавить метод для формирования данных ответа:
<?php
namespace App\Http\Responses;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\Resources\Json\JsonResource;
use Symfony\Component\HttpFoundation\Response;
class ApiSuccessResponse implements Responsable
{
/**
* @param array|JsonResource $data
* @param int $code
* @param array $headers
*/
public function __construct(
private readonly array|JsonResource $data = [],
private readonly int $code = Response::HTTP_OK,
private readonly array $headers = []
)
{
//
}
/**
* @param array|JsonResource $data
* @param int $code
* @param array $headers
* @return ApiSuccessResponse
*/
public static function make(
array|JsonResource $data = [],
int $code = Response::HTTP_OK,
array $headers = []
): ApiSuccessResponse
{
return new self($data, $code, $headers);
}
/**
* @param $request
* @return Response
*/
public function toResponse($request): Response
{
return response()->json(
$this->makeContent(),
$this->code,
$this->headers
);
}
/**
* @return array
*/
private function makeContent(): array
{
return array_merge([
'success' => true,
], $this->data);
}
}
Пример использования:
<?php
namespace App\Http\Controllers;
use App\Http\Responses\ApiSuccessResponse;
use Illuminate\Contracts\Support\Responsable;
class UserController extends Controller
{
public function store(CreateUserRequest $request): Responsable
{
$user = User::create($request->all());
return ApiSuccessResponse::make([
'user' => $user,
]);
}
}
Создание ответа об ошибке 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);
}
}
С помощью этого класса мы можем гарантировать, что:
- Ответ всегда будет в формате JSON.
- Мы всегда вернем messageимущество.
- Информация об отладке будет добавлена при необходимости.
- При необходимости мы можем настроить HTTP-код и заголовки.
Теперь вы можете использовать этот ответ для своих конечных точек:
class UserController extends Controller
{
public function store(CreateUserRequest $request): JsonResponse
{
try {
$user = User::create($request->all());
return ApiSuccessResponse::make([
'user' => $user,
]);
} catch (Throwable $exception) {
return new ApiErrorResponse(
'An error occurred while trying to create the user',
$exception
);
}
}
}
Заключение
В этой статье мы узнали, как использовать классы Laravel Responsable для создания стандартизированных ответов API для наших приложений, чтобы улучшить их качество и удобство обслуживания. Примеры в этой статье предназначены только для того, чтобы показать, как использовать эту функцию, но вы можете настроить собственные классы ответов в соответствии с потребностями ваших проектов.