You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Slim 4结合PHP-DI:如何将DTO作为控制器方法参数注入?

Slim Framework v4 中请求DTO注入控制器的最佳实践

核心问题解决思路

Slim v4 默认不会将 ServerRequestInterface 实例存入DI容器,导致直接注入DTO时容器无法解析依赖。解决关键是通过中间件将当前请求注入容器,同时为DTO配置工厂逻辑实现自动实例化与参数映射、验证。


步骤1:实现带验证的DTO类

DTO负责从请求中提取、映射参数,并做合法性校验,示例如下:

use Psr\Http\Message\ServerRequestInterface;
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints as Assert;

class BaseDto
{
    #[Assert\NotBlank(message: 'name不能为空')]
    #[Assert\Length(min: 2, max: 20, message: 'name长度需在2-20字符之间')]
    public string $name;

    #[Assert\GreaterThanOrEqual(value: 18, message: '年龄必须大于等于18')]
    public int $age;

    public function __construct(ServerRequestInterface $request)
    {
        // 合并GET与POST参数
        $requestData = array_merge(
            $request->getQueryParams(),
            $request->getParsedBody() ?? []
        );

        // 映射参数到DTO属性
        $this->name = $requestData['name'] ?? '';
        $this->age = (int)($requestData['age'] ?? 0);

        // 执行验证
        $this->validate();
    }

    private function validate(): void
    {
        $validator = Validation::createValidator();
        $violations = $validator->validate($this);

        if (count($violations) > 0) {
            $errors = [];
            foreach ($violations as $violation) {
                $errors[] = $violation->getMessage();
            }
            throw new \RuntimeException(implode('; ', $errors));
        }
    }
}

注:这里使用symfony/validator做验证,需先通过Composer安装:composer require symfony/validator


步骤2:添加请求注入中间件

创建中间件将当前请求存入DI容器,让容器能够获取ServerRequestInterface实例:

use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Psr7\Response;

class RequestInjectorMiddleware
{
    public function __invoke(ServerRequestInterface $request, RequestHandlerInterface $handler): Response
    {
        /** @var ContainerInterface $container */
        $container = $request->getAttribute('container');
        // 将当前请求存入容器
        $container->set(ServerRequestInterface::class, $request);

        return $handler->handle($request);
    }
}

在Slim应用中注册该中间件:

$app->add(RequestInjectorMiddleware::class);

步骤3:配置DI容器解析DTO

在php-di的容器定义中,为BaseDto注册工厂逻辑,让容器自动从请求实例化DTO:

use DI\ContainerBuilder;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface;

$containerBuilder = new ContainerBuilder();

$containerBuilder->addDefinitions([
    BaseDto::class => function (ContainerInterface $container) {
        $request = $container->get(ServerRequestInterface::class);
        return new BaseDto($request);
    },
]);

$container = $containerBuilder->build();
$app = Slim\Factory\AppFactory::createFromContainer($container);

步骤4:控制器直接注入DTO

现在可以在控制器方法中直接声明DTO参数,容器会自动完成注入:

use Psr\Http\Message\ResponseInterface;

class TestController
{
    public function index(BaseDto $myDto, ResponseInterface $response): ResponseInterface
    {
        // 直接使用DTO中的验证后参数
        $responseData = [
            'status' => 'success',
            'data' => [
                'name' => $myDto->name,
                'age' => $myDto->age
            ]
        ];

        $response->getBody()->write(json_encode($responseData));
        return $response->withHeader('Content-Type', 'application/json');
    }
}

额外优化:全局异常处理

为了统一处理DTO验证失败的异常,添加全局异常中间件返回标准化错误响应:

use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Psr7\Response;

class ExceptionHandlerMiddleware
{
    public function __invoke(ServerRequestInterface $request, RequestHandlerInterface $handler): Response
    {
        try {
            return $handler->handle($request);
        } catch (\RuntimeException $e) {
            $response = new Response();
            $response->getBody()->write(json_encode([
                'status' => 'error',
                'message' => $e->getMessage()
            ]));
            return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
        }
    }
}

注册到应用中(注意要放在其他中间件之前):

$app->add(ExceptionHandlerMiddleware::class);

内容的提问来源于stack exchange,提问作者CRaSH3k

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 13:35:03