timostamm/symfony-twirp-handler

帮助在 Symfony 应用中实现 Twirp

v0.0.5 2022-12-12 08:55 UTC

This package is auto-updated.

Last update: 2024-09-02 09:59:00 UTC


README

帮助在 Symfony 应用中实现 Twirp。

最简单的方法

假设你在 proto 文件中定义了此服务

syntax = "proto3";
service SearchService {
    rpc Search (SearchRequest) returns (SearchResponse);
}

首先使用 protoc 生成 PHP 代码,例如

protoc --proto_path protos/ --php_out out-php protos/example-service.proto

然后为对应的 Twirp 路由创建控制器

// SearchServiceController.php
/**
 * @Route("/twirp/SearchService")
 */
class SearchServiceController
{
    
    use TwirpControllerTrait;

    /**
     * @Route("/MakeHat")
     */
    public function makeHat(Request $request): Response
    {
        /** @var SearchRequest $input */
        $input = $this->readTwirp($request, SearchRequest::class);
        // ...
        
        $output = new SearchResponse();
        // ...

        return $this->writeTwirp($request, $output);
    }

}

Twirp 路由 URL 构造如下:twirp/{proto 包名}.{proto 服务名}/{proto 方法名}

readTwirp()writeTwirp() 将为您处理内容协商。

正确处理错误

Twirp 有自己的错误格式。要自动转换异常,可以使用 TwirpErrorSubscriber

// services.yaml
SymfonyTwirp\TwirpErrorSubscriber:
    arguments:
        $requestTagAttribute: "_request_id"
        $debug: '%kernel.debug%'
        $prefix: "twirp"

如果您已设置此订阅者,还可以抛出您自己的 TwirpError(完全控制 twirp 错误代码和元数据)。有关 TwirpErrorSubscriber 参数的文档,请查看 PHPdoc。

高级用法

为每个 RPC 编写 symfony 路由既繁琐又容易出错。
启用 php_generic_services 为每个服务生成 PHP 接口

// example-service.proto
syntax = "proto3";

option php_generic_services = true;

service SearchService {
    rpc Search (SearchRequest) returns (SearchResponse);
}

从此文件开始,protoc 生成通用服务接口 SearchServiceInterface.php。创建一个新的类 SearchService 并实现该接口

// SearchService.php
class SearchService implements SearchServiceInterface
{

    public function search(SearchRequest $request)
    {
        $response = new SearchResponse();
        $response->setHits(['a', 'b', 'c']);
        return $response;
    }

}

要使用 Twirp 提供此服务,您可以使用 TwirpHandler。您只需要为一个或多个服务提供一个路由。处理程序负责路由、内容协商、解析和序列化,并自动调用您服务上的正确方法。

创建一个匹配所有 twirp/ 请求的路由,并如下使用 TwirpHandler

// TwirpController.php
/**
 * @Route( path="twirp/{serviceName}/{methodName}" )
 */
public function execute(RequestInterface $request, string $serviceName, string $methodName): Response
{
    $resolver = new ServiceResolver();
    
    $resolver->registerInstance(
        SearchServiceInterface::class, // the interface generated by protoc 
        new SearchService() // your implementation of the interface
    );
    
    // alternatively, you can register a factory
    // $resolver->registerFactory(SearchServiceInterface::class, function() {
    //    return new SearchService();
    // });
    
    // .. or a PSR container with $resolver->registerContainer()

    $handler = new TwirpHandler($resolver);

    return $handler->handle($serviceName, $methodName, $request);
}