stefanorg / graphql-middleware
容器互操作性的 GraphQL 中间件
Requires
- php: ^5.6 || ^7.0
- container-interop/container-interop: ^1.1
- respect/validation: ^1.1
- youshido/graphql: ~1.4
- zendframework/zend-diactoros: ^1.3
- zendframework/zend-servicemanager: ^2.7.3 || ^3.0
Requires (Dev)
- phpunit/phpunit: ^4.8
- squizlabs/php_codesniffer: ^2.5
This package is not auto-updated.
Last update: 2024-09-24 23:26:20 UTC
README
这是一个基于 Youshido/GraphQL 的 GraphQL 纯 PHP 实现的 GraphQL 中间件实现。
使用中间件
use GraphQLMiddleware\Execution\Processor;
use App\GraphQL\MySchema;
use GraphQLMiddleware\GraphQLMiddleware;
...
...
//instantiate the graphql schema
$schema = new MySchema();
//get your container implementing container-interop
$container = get_your_container;
//init processor
$processor = new Processor($container, $schema);
$graphqlMiddleware = new GraphQLMiddleware($processor)
$app->pipe("/graphql", $graphqlMiddleware)
如果您的容器支持工厂,您可以使用提供的那些。
例如,如果您使用 zend expressive
,您可以通过 配置管理器
利用提供的配置包。
$configManager = new ConfigManager([
GraphQLMiddleware\ModuleConfig::class,
new PhpFileProvider('config/autoload/{{,*.}global,{,*.}local}.php'),
]);
与 GraphQL 中间件交互
对于针对 /graphql
URI 或包含 Content-Type: application/graphql
标头的每个请求。目前仅支持 GET
和 POST
方法。
工厂
Schema\SchemaFactory
工厂用于从数组、类或容器中实例化模式对象Execution\ProcessorFactory
工厂用于实例化 GraphQL 处理器,具有ContainerAwareInterface
能力,允许您编写带有直接从容器中获取服务能力的解析器类。Resolver\ContainerAwareResoverFactory
工厂用于自动将容器注入到您的解析器类中
容器感知字段
您可以在您的模式中编写使字段感知容器的代码,这样您可以直接从字段中检索容器引用,并从容器中拉取任何您需要用于 resolve
字段逻辑的依赖项。
假设我们有一个 TodoField
,我们正在实现一个用于将待办事项存储到我们后端的突变 AddTodoField
。
<?php
namespace App\GraphQL\Mutation\Todo;
use App\GraphQL\Type\TodoType;
use App\Service\TodoService;
use GraphQLMiddleware\Field\AbstractContainerAwareField;
use Youshido\GraphQL\Config\Field\FieldConfig;
use Youshido\GraphQL\Execution\ResolveInfo;
use Youshido\GraphQL\Type\ListType\ListType;
use Youshido\GraphQL\Type\NonNullType;
use Youshido\GraphQL\Type\Scalar\StringType;
class AddTodoField extends AbstractContainerAwareField
{
public function build(FieldConfig $config)
{
$config->addArguments([
'title' => new NonNullType(new StringType()),
'tags' => new ListType(new StringType())
]);
}
public function resolve($value, array $args, ResolveInfo $info)
{
// pull our service from the db
$todoService = $this->getContainer()->get(TodoService::class);
// let the service do his job
return $todoService->create($args['title']);
}
public function getType()
{
return new ListType(new TodoType());
}
public function getName()
{
return 'add';
}
}
为了从我们的服务中检索待办事项,我们可以这样编写查询字段
namespace App\GraphQL\Query\Todo;
use App\GraphQL\Type\TodoType;
use App\Serivice\TodoService;
use GraphQLMiddleware\Field\AbstractContainerAwareField;
use Youshido\GraphQL\Execution\ResolveInfo;
use Youshido\GraphQL\Type\ListType\ListType;
class TodosField extends AbstractContainerAwareField
{
public function getType()
{
return new ListType(new TodoType());
}
public function resolve($value, array $args, ResolveInfo $info)
{
/** @var $service TodoService */
$service = $this->getContainer()->get(TodoService::class);
return $service->findAll();
}
}
业务逻辑验证
GraphQL 提供了对模式类型的验证,但目前没有一条清晰的路径来执行应用程序验证。
验证用户数据之前将其发送到您的后端是您的责任。让我们想象在我们的待办事项示例中,title
字段不能包含空格,且长度必须小于 10 个字符。
GraphQLMiddleware 提供了一些直接在您的 type
中调用 resolve
方法之前执行应用程序验证的便利性。
为此,您可以实现 GraphQLMiddleware\Validation\ValidatableInterface
。此接口已经在 AbstractContainerAwareField
中实现。
让我们修改我们的突变字段 AddTodoField
以对 title
字段实施验证要求。
GraphQLMiddleware 使用 respect/validation
库来处理验证。
为了修改我们的 AddTodoField
,我们必须
- 提供验证规则,实现
ValidatableInterface::getValidationRules
方法
use Respect\Validation\Validator as v;
class AddTodoField extends AbstractContainerAwareField
{
...
...
public function resolve($value, array $args, ResolveInfo $info)
{
// the $args array is validate based on the
// validation rules provided by the getValidationRules method
return $this->getContainer()->get(TodoResolver::class)->create($args['title']);
}
public function getValidationRules()
{
return [
'title' => v::stringType()->alpha()->length(1,10)->noWhitespace()
];
}
...
...
}
这样,处理器在实际上调用 resolve
方法之前,会调用 validate
方法,并且只有当一切顺利时才会调用 resolve
方法。
AbstractContainerAwareField
提供了 validate
方法的默认实现。因此,您不需要实现它,只需提供验证规则即可。
验证错误响应
服务器在出现验证错误时提供了关于这些错误的有用信息。实际的实现将把这些信息推送到 errors
有效负载中,如下所示
有人认为
errors
有效负载是为了系统错误,如语法
错误,但个人认为这是一个放置关于请求正在发生什么的好地方。
使用我们的 todos
示例,如果我们尝试添加待办事项
mutation{
add(title: "this is not @ very good title") {
id
}
}
服务器响应是
{
"data": {
"add": null
},
"errors": [
{
"message": "Bad Request. Validation failed.",
"validation_errors": {
"add": {
"title": [
"\"this is not @ very good title\" alphanumeric only allowed",
"\"this is not @ very good title\" no withespace allowed"
]
}
},
"code": 403
}
]
}
在 errors
有效载荷中,我们包含了 validation_errors
信息,这些信息为我们提供了关于请求中错误的详细信息。