首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何制作自定义PSR-7 ResponseInterface以减少样板?

如何制作自定义PSR-7 ResponseInterface以减少样板?
EN

Stack Overflow用户
提问于 2021-11-12 19:26:09
回答 2查看 519关注 0票数 9

我正在使用Slim 4框架实现一个服务,它几乎总是返回JSON响应。我试图让所有的响应保持一致,结构类似于以下内容:

代码语言:javascript
复制
{
    "status": "success",
    "data": {"foo": "bar"} // the actual data relevant to the request
    "messages": []
}

在最基本的格式中,这是我需要做的代码,以便做出如下响应:

代码语言:javascript
复制
public function __invoke(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface {
    
    // Do something 

    $response
        ->getBody()
        ->write(json_encode([
            'status' => 'success',
            'data' => [
                'foo' => 'bar'
            ],
            'messages' => [],
        ]));
    return $response
        ->withHeader('Content-Type', 'application/json')
        ->withStatus(200);
}

现在,我一直在使用一个基本助手类,它实际上将大部分模板封装成一些静态函数,因此我可以编写如下响应:

代码语言:javascript
复制
public function __invoke(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface {
    
    // Do something 

    $data = ['foo' => 'bar'];

    return APIResponse::success($response, $data)->into();
}

但是,现在我遇到了一个问题,我希望使响应稍微复杂一些,这需要额外的依赖项,例如自定义序列化器类。天真的选择是继续将附加的依赖项传递给APIResponse::success($response, $serializer, ..., $data),但这显然是不好的,而不是一个好的长期选项。

我想到的另一个选项是创建一个APIResponseFactory,它将接受构造函数中的任何依赖项,并通过PHP进行填充。这样会更干净一些,但是每个路由处理程序都需要注入工厂,而且每次我仍然需要手动传递$response

代码语言:javascript
复制
return $responseFactory->success($response, $data);

因此,我现在考虑的是构建一个实现ResponseInterface的自定义类,从而允许我在样板帮助程序中自动构建每个请求处理程序。我查看了我的项目中使用的当前PSR7 ResponseInterface实现,代码注释提到了这个类永远不应该扩展。,并建议使用Decorator模式。因此,这是一个基本的伪码实现,我为我目前的想法。

代码语言:javascript
复制
class MyCustomResponse implements ResponseInterface {

    private $serializer; 

    private $actualResponse;

    // any other dependencies

    public function __construct(ResponseInterface $actualResponse, Serializer $serializer /*, other dependencies */) {
        $this->actualResponse = $actualResponse;
        $this->serializer = $serializer;
    }

    // Use this class as a decorator and pass all ResponseInterface calls to the external implementation
    // EDIT: It looks like I can't use `__call` to fulfill the interface, so I'd need to manually define to functions, but you get the gist. 
    public function __call($name, $args) {
        return $this->actualResponse->$name(...$args);
    }

    public function success($data) {
        $this->actualResponse
            ->getBody()
            ->write($this->serializer->serialize([
                'status' => 'success',
                'data' => $data,
                'messages' => [],
            ]));
        $this->actualResponse
            ->withHeader('Content-Type', 'application/json')
            ->withStatus(200);
        return $this;
    }
}

因此,我(希望)能够(希望)得到这样的答复:

代码语言:javascript
复制
public function __invoke(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface {
    $data = ['foo' => 'bar'];
    return $response->success($data);
}

我的实际问题:是为PSR-7响应处理程序实现自定义帮助方法的正确方法吗?有更好的办法吗?编写助手函数是否像这种糟糕的实践?由于缺乏更好的描述,低层次和冗长的PSR-7接口,这让我担心写这样的包装器在某种程度上违背了标准的意图。有没有其他方法来遵循标准,但减少样板和保持一致的反应?

EN

回答 2

Stack Overflow用户

发布于 2021-11-17 21:46:03

一个‘好的’控制器动作return $response->success($data);可以通过

这是个糟糕的做法

  • {"status":"", "data":[], "messages": []}格式可能会更改
  • 出现文件、流等响应

忘记了ResponseInterface $response参数,它是如何用array $args参数完成的。

将响应工厂注入每个控制器,因此您的操作将类似于

代码语言:javascript
复制
public function __invoke(ServerRequestInterface $request): ResponseInterface 
{
    // Do something 

    return $this->responseFactory->createJson($data);
    // OR
    //return $this->responseFactory->createSomethingElse();
}
票数 2
EN

Stack Overflow用户

发布于 2021-11-29 22:50:29

您已经多次编写了希望避免为每个控制器注入依赖项的文章,我认为您不应该避免使用依赖项注入,因为它不是那么多的工作负载,它具有许多优点,例如:

  • 可测试性您可以在真正测试所有东西时模拟所有依赖项
  • 因此,模块化使您的类与其他类隔离,不依赖于全局的和不明确的依赖关系。
  • maintainability,您对这个类需要做什么有一个明确的定义,我向您保证,这在几年后将是必不可少的。

依赖注入容器确实有助于编写大量用于应用di的代码。

我的控制器通常看起来像这样:

代码语言:javascript
复制
    class MyController implements RequestHandlerInterface
    {
        private Presenter $presenter;
       
        public function __construct(
            Presenter $presenter
        ) {
            $this->presenter = $presenter;
        }
    
        public function handle(ServerRequestInterface $request): ResponseInterface
        {
            return $this->presenter->present(
                200, 
                [
                    'status' => 'success',
                    'data' => $data,
                    'messages' => [],
                ],
                //other presenter arguments here such as a class instance that defines a standard Metadata object to be included in all Responses
            );
        }

你的推荐人也可以有它的依赖性,它都保持干净和整洁。

Slim 4路由设置将类似于:

代码语言:javascript
复制
$app->get('/route_name', MyController::class);

这种方式是DI容器方式注入一切和路由页面也是干净和整洁的!

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69948134

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档