Hyperf控制器方法参数支持自动注入RequestInterface、ResponseInterface及已绑定的单实现服务类;多实现接口需通过name显式指定,否则报错。

Hyperf 控制器方法参数能自动注入,前提是类型提示正确、上下文存在对应实例,且该类型被容器识别为“可解析的协程局部对象”或“已绑定的服务”。不是所有类型都能自动注入,也不是写了类型提示就一定成功。
哪些类型能直接类型提示注入?
以下类型在控制器方法参数中写明类型提示,Hyperf 会自动提供实例:
-
Hyperf\HttpServer\Contract\RequestInterface或Psr\Http\Message\ServerRequestInterface:每次请求唯一的 PSR-7 请求对象,来自协程 Context,非全局共享 -
Hyperf\HttpServer\Contract\ResponseInterface:响应构造器,同样按协程隔离 - 已注册到 DI 容器的类或接口(如
App\Service\UserService),但仅限单实现;若接口有多个实现,必须显式指定name
注意:$_GET/$_POST 等超全局变量不会被注入,也不该依赖——它们在协程并发下不安全。
为什么写了 ServerRequestInterface 却拿不到数据?
常见原因不是类型写错,而是中间件链中断或 Context 被覆盖:
- 自定义中间件里调用了
Context::set(ServerRequestInterface::class, ...)但传入了错误实例(比如 new 出来的空对象) - 中间件提前
return $response,导致后续中间件(包括 CoreMiddleware)没执行,ServerRequest没存进 Context - 在非 HTTP Server 场景下调用控制器(如 CLI 命令、定时任务),此时没有请求上下文,
ServerRequestInterface无法解析,会抛出ContainerException
验证方式:在方法内加 var_dump($request instanceof ServerRequestInterface);,返回 false 就说明注入失败,不是数据为空。
接口多实现时怎么安全注入?
当一个接口(如 App\Contract\SmsServiceInterface)绑定了多个实现类,默认类型提示会报错:Cannot resolve type "App\Contract\SmsServiceInterface"。必须显式指定使用哪一个:
- 在
config/autoload/dependencies.php中注册命名绑定:'App\Contract\SmsServiceInterface@aliyun' => App\Service\AliyunSmsService::class - 控制器中用
#[Inject(name: 'aliyun')]注入属性,或运行时用$this->container->make(SmsServiceInterface::class, ['name' => 'aliyun']) - 别漏掉
@var注解或属性类型声明,否则#[Inject]不生效
容易踩的坑是只配了主键 'App\Contract\SmsServiceInterface',没配 @aliyun 后缀项,导致 name 参数匹配失败。
构造函数注入和方法参数注入的区别
两者都走 DI 容器,但生命周期和适用场景不同:
- 构造函数注入的对象是单例,整个进程生命周期内复用,适合无状态服务(如数据库连接、缓存客户端)
- 方法参数注入的对象按请求/协程隔离,适合带请求上下文的数据(如
RequestInterface、ResponseInterface),每次调用都是新实例或协程专属实例 - 不要在构造函数里依赖
ServerRequestInterface—— 它不存在于进程启动时,容器无法提前创建控制器实例
最常被忽略的一点:方法参数注入的 RequestInterface 是“当前请求”的快照,一旦控制器方法返回,它就不再更新;若你在异步协程里继续用它,可能读到过期或空值。


















