Início rápido
O Yii trabalha com APIs JSON usando os mesmos objetos de requisição e resposta PSR-7 das páginas web. Para endpoints de API, o fluxo comum é:
- Ler o corpo da requisição quando o cliente envia JSON.
- Mapear os dados da requisição para um objeto de entrada tipado.
- Validar o objeto de entrada.
- Retornar uma resposta cujos dados são formatados como JSON.
Este guia assume que você usa o template de aplicação API. Ele já inclui a leitura do corpo da requisição, a resolução da entrada da requisição, respostas de dados em JSON e exemplos de presenters. Para aplicações completas com outras estruturas, veja Aplicações demo.
Criando a entrada da requisição
Use o yiisoft/input-http para descrever os dados da requisição como um objeto de entrada tipado. Adicione #[FromBody] quando todos os valores devem vir do corpo da requisição já interpretado.
Crie src/Api/Post/CreatePostInput.php:
<?php
declare(strict_types=1);
namespace App\Api\Post;
use Yiisoft\Input\Http\AbstractInput;
use Yiisoft\Input\Http\Attribute\Data\FromBody;
use Yiisoft\Validator\Rule\Length;
use Yiisoft\Validator\Rule\Required;
#[FromBody]
final class CreatePostInput extends AbstractInput
{
#[Required]
#[Length(min: 2, max: 100)]
public string $title = '';
#[Required]
public string $content = '';
}Para uma requisição como:
POST /posts HTTP/1.1
Content-Type: application/json
{"title": "First post", "content": "Text"}CreatePostInput recebe title e content do corpo JSON decodificado.
Se a maioria dos valores deve vir do corpo da requisição, mas um valor precisa vir de outra parte da requisição, combine #[FromBody] com atributos de parâmetro.
Por exemplo, crie src/Api/Post/UpdatePostInput.php:
<?php
declare(strict_types=1);
namespace App\Api\Post;
use Yiisoft\Input\Http\AbstractInput;
use Yiisoft\Input\Http\Attribute\Data\FromBody;
use Yiisoft\Input\Http\Attribute\Parameter\Query;
use Yiisoft\Validator\Rule\Length;
use Yiisoft\Validator\Rule\Required;
#[FromBody]
final class UpdatePostInput extends AbstractInput
{
public function __construct(
#[Query('id')]
public int $id = 0,
#[Required]
#[Length(min: 2, max: 100)]
public string $title = '',
#[Required]
public string $content = '',
) {}
}Para uploads de arquivos enviados como multipart/form-data, mapeie os arquivos enviados com #[UploadedFiles].
Por exemplo, crie src/Api/Post/UploadImageInput.php:
<?php
declare(strict_types=1);
namespace App\Api\Post;
use Yiisoft\Input\Http\AbstractInput;
use Yiisoft\Input\Http\Attribute\Parameter\Body;
use Yiisoft\Input\Http\Attribute\Parameter\UploadedFiles;
final class UploadImageInput extends AbstractInput
{
public function __construct(
#[Body]
public string $title,
#[UploadedFiles('image')]
public mixed $image,
) {}
}Criando uma action
O RequestInputParametersResolver permite que uma action declare o tipo de objetos de entrada da requisição diretamente. No template de aplicação API, ele já está configurado em config/web/di/application.php.
Crie src/Api/Post/CreatePostAction.php:
<?php
declare(strict_types=1);
namespace App\Api\Post;
use App\Api\Shared\ResponseFactory;
use Psr\Http\Message\ResponseInterface;
use Yiisoft\Http\Status;
final readonly class CreatePostAction
{
public function __invoke(
CreatePostInput $input,
ResponseFactory $responseFactory,
): ResponseInterface {
$result = $input->getValidationResult();
if (!$result->isValid()) {
return $responseFactory->failValidation($result);
}
return $responseFactory
->success([
'title' => $input->title,
'content' => $input->content,
])
->withStatus(Status::CREATED);
}
}Você pode validar a entrada na action como mostrado acima. Outra opção é configurar o RequestInputParametersResolver para lançar Yiisoft\Input\Http\InputValidationException e converter essa exceção em uma resposta JSON em um único tratador de erros. O template de aplicação API usa essa abordagem centralizada.
Retornando respostas JSON
Para endpoints pequenos, você pode escrever o JSON em uma resposta PSR-7 manualmente, como mostrado em Respondendo com JSON. Para APIs, prefira o yiisoft/data-response, que formata os dados da resposta e define o header Content-Type.
O template de aplicação API mantém a formatação das respostas em src/Api/Shared/ResponseFactory.php:
<?php
declare(strict_types=1);
namespace App\Api\Shared;
use App\Api\Shared\Presenter\AsIsPresenter;
use App\Api\Shared\Presenter\PresenterInterface;
use App\Api\Shared\Presenter\ValidationResultPresenter;
use Psr\Http\Message\ResponseInterface;
use Yiisoft\DataResponse\ResponseFactory\DataResponseFactoryInterface;
use Yiisoft\Http\Status;
use Yiisoft\Validator\Result;
final readonly class ResponseFactory
{
public function __construct(
private DataResponseFactoryInterface $dataResponseFactory,
) {}
public function success(
array|object|null $data = null,
PresenterInterface $presenter = new AsIsPresenter(),
): ResponseInterface {
return $this->dataResponseFactory->createResponse([
'status' => 'success',
'data' => $presenter->present($data),
]);
}
public function failValidation(Result $result): ResponseInterface
{
return $this->dataResponseFactory
->createResponse([
'status' => 'failed',
'error_message' => 'Validation failed.',
'error_data' => (new ValidationResultPresenter())->present($result),
])
->withStatus(Status::UNPROCESSABLE_ENTITY);
}
}Presenters são abstrações da camada de aplicação que transformam objetos da aplicação em arrays de saída da API. Mantenha-os separados das entidades de domínio, para que mudar uma resposta da API não force mudanças nos objetos de negócio.
Crie src/Api/Post/PostPresenter.php:
<?php
declare(strict_types=1);
namespace App\Api\Post;
use App\Api\Shared\Presenter\PresenterInterface;
/**
* @implements PresenterInterface<Post>
*/
final readonly class PostPresenter implements PresenterInterface
{
public function present(mixed $value): array
{
return [
'id' => $value->id,
'title' => $value->title,
'content' => $value->content,
];
}
}O template de API contém uma implementação mais completa da response factory e dos presenters.
Roteando um endpoint
Registre as rotas da API em config/common/routes.php:
use App\Api\Post\CreatePostAction;
use Yiisoft\Http\Method;
use Yiisoft\Router\Route;
return [
Route::methods([Method::POST], '/posts')
->action(CreatePostAction::class)
->name('api/posts/create'),
];Envie JSON definindo Content-Type: application/json. Peça respostas JSON com Accept: application/json.
curl -X POST http://localhost:8080/posts \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{"title":"First post","content":"Text"}'Erros de validação são retornados como JSON com status HTTP 422 Unprocessable Entity. Uma criação bem-sucedida normalmente retorna 201 Created.
Aplicações existentes
Se você for adicionar endpoints de API JSON a uma aplicação web existente, primeiro instale os pacotes de API:
composer require yiisoft/input-http yiisoft/request-body-parser yiisoft/data-responseSe você usa Docker:
make composer require yiisoft/input-http yiisoft/request-body-parser yiisoft/data-responseEm config/web/di/application.php, adicione o RequestBodyParser antes do Router, para que as requisições JSON sejam lidas antes de as actions rodarem:
use Yiisoft\Request\Body\RequestBodyParser;
use Yiisoft\Router\Middleware\Router;
return [
RequestBodyParser::class,
Router::class,
];No mesmo arquivo, adicione o middleware de resposta de dados JSON antes do tratamento de erros e do roteamento:
use Yiisoft\DataResponse\Formatter\JsonFormatter;
use Yiisoft\DataResponse\Middleware\ContentNegotiatorDataResponseMiddleware;
return [
static fn() => new ContentNegotiatorDataResponseMiddleware(
formatters: [
'application/json' => new JsonFormatter(),
],
fallback: new JsonFormatter(),
),
// tratamento de erros, leitor do corpo da requisição, router e middleware de não encontrado
];Também em config/web/di/application.php, configure os resolvedores de parâmetros das actions:
use Yiisoft\Definitions\Reference;
use Yiisoft\Input\Http\HydratorAttributeParametersResolver;
use Yiisoft\Input\Http\RequestInputParametersResolver;
use Yiisoft\Middleware\Dispatcher\CompositeParametersResolver;
use Yiisoft\Middleware\Dispatcher\ParametersResolverInterface;
return [
ParametersResolverInterface::class => [
'class' => CompositeParametersResolver::class,
'__construct()' => [
Reference::to(HydratorAttributeParametersResolver::class),
Reference::to(RequestInputParametersResolver::class),
],
],
];Use a configuração do template de aplicação API como referência completa para a ordem dos middlewares e a configuração dos resolvedores.