HTTP请求和响应是Web编程中的两个基本对象。与外部API通信的所有客户端都使用某种形式的HTTP客户端。许多库耦合到一个特定客户端或自己实现客户端和/或适配器层。这会导致错误的库设计,版本冲突或与库域无关的代码。
感谢PSR-7,我们知道HTTP请求和响应的理想状态,但没有定义如何发送请求和接收响应。HTTP客户端的通用接口将允许库与特定实现分离。
此PSR未涵盖异步请求的原因是缺少Promises的通用标准。承诺足够复杂,它们应该得到自己的规范,不应该包含在这个规范中。
一旦接受Promise PSR,就可以在单独的PSR中定义用于异步请求的单独接口。异步请求的方法签名必须与同步请求的方法签名不同,因为异步调用的返回类型将是Promise。因此,该PSR是向前兼容的,并且客户端将能够基于他们希望公开的特征来实现一个或两个接口。
此PSR的目的是为库开发人员提供具有明确定义的行为的HTTP客户端。库应该能够使用任何兼容的客户端而无需特殊代码来处理客户端实现细节(Liskov替换原则)。PSR不会尝试限制或定义如何配置HTTP客户端。
另一种方法是将配置传递给客户端。这种方法会有一些缺点:
主要接口行为由方法定义sendRequest(RequestInterface $request): ResponseInterface
。
虽然send()
已经提出了较短的方法名称,但现有的和非常常见的HTTP客户端(如Guzzle)已经使用过这个名称。因此,如果他们要采用这个标准,他们可能需要打破向后兼容性才能实现规范。sendRequest()
相反,通过定义,我们确保他们可以采用而不会立即中断BC。
域异常NetworkExceptionInterface
并RequestExceptionInterface
定义一个彼此非常相似的契约。选择的方法是不让它们相互扩展,因为继承在域模型中没有意义。A RequestExceptionInterface
根本就不是
NetworkExceptionInterface
。
已经讨论了允许扩展RequestAwareException
和/或ResponseAwareException
接口的异常,但这是一个不应该采取的便利捷径。人们应该抓住具体的例外并相应地处理它们。
在定义异常时,可以更精细。例如,TimeOutException
和HostNotFoundException
可能的亚型NetworkExceptionInterface
。所选择的方法不是定义这样的子类型,因为消费库中的异常处理在大多数情况下在这些异常之间不会有所不同。
最初的想法是允许客户端配置为针对HTTP状态为4xx和5xx的响应抛出异常。这种方法是不可取的,因为消费库必须检查4xx和5xx响应两次:首先,通过验证响应的状态代码,然后通过捕获潜在的异常。
为了使规范更具可预测性,决定HTTP客户端永远不会抛出4xx和5xx响应的异常。
该规范对中间件或想要包装/装饰HTTP客户端的类没有任何限制。如果装饰类也实现了ClientInterface
那么它也必须遵循规范。
很容易允许配置或向HTTP客户端添加中间件,以便它可以遵循重定向或抛出异常。如果这是应用程序开发人员的决定,他们明确表示他们想要破坏规范。这是应用程序开发人员应该处理的问题(或功能)。第三方库绝不能假设HTTP客户端违反了规范。
HTTP客户端PSR受到php-http团队的启发和创建。早在2015年,他们就将HTTPlug创建为HTTP客户端的通用接口。他们想要第三方库可以使用的抽象,以便不依赖于特定的HTTP客户端实现。2016年1月标注了稳定版本,此后该项目被广泛采用。在初始稳定版本之后的两年内,下载量超过300万次,是时候将这种“事实上的”标准转换为正式规范。
以下是工作组通过审核期的两种实施方式: