CakePHP 包含一个 PSR-18 兼容的 HTTP 客户端,可用于发送请求。它是与 Web 服务和远程 API 通信的绝佳方式。
发送请求简单直接。发送 GET 请求如下所示
use Cake\Http\Client;
$http = new Client();
// Simple get
$response = $http->get('http://example.com/test.html');
// Simple get with querystring
$response = $http->get('http://example.com/search', ['q' => 'widget']);
// Simple get with querystring & additional headers
$response = $http->get('http://example.com/search', ['q' => 'widget'], [
'headers' => ['X-Requested-With' => 'XMLHttpRequest'],
]);
发送 POST 和 PUT 请求同样简单
// Send a POST request with application/x-www-form-urlencoded encoded data
$http = new Client();
$response = $http->post('http://example.com/posts/add', [
'title' => 'testing',
'body' => 'content in the post',
]);
// Send a PUT request with application/x-www-form-urlencoded encoded data
$response = $http->put('http://example.com/posts/add', [
'title' => 'testing',
'body' => 'content in the post',
]);
// Other methods as well.
$http->delete(/* ... */);
$http->head(/* ... */);
$http->patch(/* ... */);
如果您已创建 PSR-7 请求对象,则可以使用 sendRequest()
发送它
use Cake\Http\Client;
use Cake\Http\Client\Request as ClientRequest;
$request = new ClientRequest(
'http://example.com/search',
ClientRequest::METHOD_GET
);
$http = new Client();
$response = $http->sendRequest($request);
您可以通过在数组中包含文件句柄来在请求主体中包含文件
$http = new Client();
$response = $http->post('http://example.com/api', [
'image' => fopen('/path/to/a/file', 'r'),
]);
文件句柄将被读取直到其结束;它在读取之前不会被倒带。
有时您需要以非常特定的方式构建请求主体。在这种情况下,您通常可以使用 Cake\Http\Client\FormData
来制作您想要的特定 Multipart HTTP 请求
use Cake\Http\Client\FormData;
$data = new FormData();
// Create an XML part
$xml = $data->newPart('xml', $xmlString);
// Set the content type.
$xml->type('application/xml');
$data->add($xml);
// Create a file upload with addFile()
// This will append the file to the form data as well.
$file = $data->addFile('upload', fopen('/some/file.txt', 'r'));
$file->contentId('abc123');
$file->disposition('attachment');
// Send the request.
$response = $http->post(
'http://example.com/api',
(string)$data,
['headers' => ['Content-Type' => $data->contentType()]]
);
在处理 REST API 时,您经常需要发送不是表单编码的请求主体。Http\Client 通过 type 选项公开这一点
// Send a JSON request body.
$http = new Client();
$response = $http->post(
'http://example.com/tasks',
json_encode($data),
['type' => 'json']
);
type
键可以是 ‘json’、‘xml’ 或完整 MIME 类型之一。在使用 type
选项时,您应该将数据提供为字符串。如果您正在进行需要查询字符串参数和请求主体的 GET 请求,您可以执行以下操作
// Send a JSON body in a GET request with query string parameters.
$http = new Client();
$response = $http->get(
'http://example.com/tasks',
['q' => 'test', '_content' => json_encode($data)],
['type' => 'json']
);
每个 HTTP 方法都接受一个 $options
参数,用于提供额外的请求信息。以下键可以在 $options
中使用
headers
- 附加头的数组
cookie
- 要使用的 Cookie 数组。
proxy
- 代理信息的数组。
auth
- 身份验证数据的数组,type
键用于委派给身份验证策略。默认情况下使用基本身份验证。
ssl_verify_peer
- 默认值为 true
。设置为 false
以禁用 SSL 证书验证(不建议)。
ssl_verify_peer_name
- 默认值为 true
。设置为 false
以在验证 SSL 证书时禁用主机名验证(不建议)。
ssl_verify_depth
- 默认值为 5。在 CA 链中遍历的深度。
ssl_verify_host
- 默认值为 true
。将 SSL 证书验证到主机名。
ssl_cafile
- 默认值为内置 cafile。覆盖以使用自定义 CA 捆绑包。
timeout
- 超时前等待的持续时间(以秒为单位)。
type
- 以自定义内容类型发送请求主体。需要 $data
作为字符串,或在进行 GET 请求时将 _content
选项设置为。
redirect
- 要遵循的重定向次数。默认值为 false
。
curl
- 额外的 curl 选项数组(如果使用 curl 适配器),例如,[CURLOPT_SSLKEY => 'key.pem']
。
options 参数始终是每个 HTTP 方法中的第三个参数。它们也可以在构造 Client
时用于创建 有范围的客户端。
Cake\Http\Client
支持几种不同的身份验证系统。开发人员可以添加不同的身份验证策略。身份验证策略在发送请求之前被调用,并允许在请求上下文中添加头。
基本身份验证的示例
$http = new Client();
$response = $http->get('http://example.com/profile/1', [], [
'auth' => ['username' => 'mark', 'password' => 'secret'],
]);
默认情况下,如果 auth 选项中没有 'type'
键,Cake\Http\Client
将使用基本身份验证。
基本身份验证的示例
$http = new Client();
$response = $http->get('http://example.com/profile/1', [], [
'auth' => [
'type' => 'digest',
'username' => 'mark',
'password' => 'secret',
'realm' => 'myrealm',
'nonce' => 'onetimevalue',
'qop' => 1,
'opaque' => 'someval',
],
]);
通过将 ‘type’ 键设置为 ‘digest’,您告诉身份验证子系统使用摘要身份验证。摘要身份验证支持以下算法
MD5
SHA-256
SHA-512-256
MD5-sess
SHA-256-sess
SHA-512-256-sess
算法将根据服务器挑战自动选择。
许多现代 Web 服务需要 OAuth 身份验证才能访问其 API。包含的 OAuth 身份验证假定您已经拥有消费者密钥和消费者密钥
$http = new Client();
$response = $http->get('http://example.com/profile/1', [], [
'auth' => [
'type' => 'oauth',
'consumerKey' => 'bigkey',
'consumerSecret' => 'secret',
'token' => '...',
'tokenSecret' => '...',
'realm' => 'tickets',
],
]);
由于 OAuth2 通常是一个标头,因此没有专门的身份验证适配器。相反,您可以使用访问令牌创建客户端
$http = new Client([
'headers' => ['Authorization' => 'Bearer ' . $accessToken],
]);
$response = $http->get('https://example.com/api/profile/1');
一些代理需要身份验证才能使用它们。通常这种身份验证是基本身份验证,但可以通过任何身份验证适配器实现。默认情况下,Http\Client 将假定基本身份验证,除非设置了 type 键
$http = new Client();
$response = $http->get('http://example.com/test.php', [], [
'proxy' => [
'username' => 'mark',
'password' => 'testing',
'proxy' => '127.0.0.1:8080',
],
]);
第二个代理参数必须是一个字符串,其中包含 IP 或不带协议的域名。用户名和密码信息将通过请求头传递,而代理字符串将通过 stream_context_create() 传递。
反复输入域名、身份验证和代理设置可能会很繁琐且容易出错。为了减少出错的机会并减轻一些繁琐的操作,您可以创建作用域客户端
// Create a scoped client.
$http = new Client([
'host' => 'api.example.com',
'scheme' => 'https',
'auth' => ['username' => 'mark', 'password' => 'testing'],
]);
// Do a request to api.example.com
$response = $http->get('/test.php');
如果您的作用域客户端只需要来自 URL 的信息,您可以使用 createFromUrl()
$http = Client::createFromUrl('https://api.example.com/v1/test');
以上操作将创建一个客户端实例,其中设置了 protocol
、host
和 basePath
选项。
创建作用域客户端时可以使用以下信息
host
basePath
scheme
proxy
auth
port
cookies
timeout
ssl_verify_peer
ssl_verify_depth
ssl_verify_host
在进行请求时,可以通过指定这些选项来覆盖任何选项。host、scheme、proxy、port 会在请求 URL 中被覆盖
// Using the scoped client we created earlier.
$response = $http->get('http://foo.com/test.php');
以上操作将替换域名、方案和端口。但是,此请求将继续使用在创建作用域客户端时定义的所有其他选项。有关支持选项的更多信息,请参见 请求方法选项。
Client
在发送请求时会发出事件。在发送请求之前会触发 HttpClient.beforeSend
事件,在发送请求之后会触发 HttpClient.afterSend
事件。您可以在 beforeSend
监听器中修改请求或设置响应。对于所有请求都会触发 afterSend
事件,即使那些响应由 beforeSend
事件设置的请求也是如此。
响应对象有许多用于检查响应数据的方法。
您可以将整个响应主体作为字符串读取
// Read the entire response as a string.
$response->getStringBody();
您还可以访问响应的流对象并使用其方法
// Get a Psr\Http\Message\StreamInterface containing the response body
$stream = $response->getBody();
// Read a stream 100 bytes at a time.
while (!$stream->eof()) {
echo $stream->read(100);
}
由于 JSON 和 XML 响应是常用的,因此响应对象提供了一种使用访问器来读取解码数据的机制。JSON 数据被解码为数组,而 XML 数据被解码为 SimpleXMLElement
树
// Get some XML
$http = new Client();
$response = $http->get('http://example.com/test.xml');
$xml = $response->getXml();
// Get some JSON
$http = new Client();
$response = $http->get('http://example.com/test.json');
$json = $response->getJson();
解码后的响应数据存储在响应对象中,因此多次访问它没有任何额外的成本。
您可以通过几种不同的方法访问头。在通过方法访问头时,头名称始终被视为不区分大小写的值
// Get all the headers as an associative array.
$response->getHeaders();
// Get a single header as an array.
$response->getHeader('content-type');
// Get a header as a string
$response->getHeaderLine('content-type');
// Get the response encoding
$response->getEncoding();
响应对象提供了一些用于检查状态代码的方法
// Was the response a 20x
$response->isOk();
// Was the response a 30x
$response->isRedirect();
// Get the status code
$response->getStatusCode();
默认情况下,Http\Client
将优先使用基于 curl
的传输适配器。如果 curl 扩展不可用,则会使用基于流的适配器。您可以使用构造函数选项强制选择传输适配器
use Cake\Http\Client\Adapter\Stream;
$http = new Client(['adapter' => Stream::class]);
HTTP 客户端在发送请求之前和之后会触发一些事件,这使您能够修改请求或响应,或者执行其他任务,例如缓存、日志记录等。
// Somewhere before calling one of the HTTP client's methods which makes a request
$http->getEventManager()->on(
'HttpClient.beforeSend',
function (
\Cake\Http\Client\ClientEvent $event,
\Cake\Http\Client\Request $request,
array $adapterOptions,
int $redirects
) {
// Modify the request
$event->setRequest(....);
// Modify the adapter options
$event->setAdapterOptions(....);
// Skip making the actual request by returning a response.
// You can use $event->setResult($response) to achieve the same.
return new \Cake\Http\Client\Response(body: 'something');
}
);
// Somewhere before calling one of the HTTP client's methods which makes a request
$http->getEventManager()->on(
'HttpClient.afterSend',
function (
\Cake\Http\Client\ClientEvent $event,
\Cake\Http\Client\Request $request,
array $adapterOptions,
int $redirects,
bool $requestSent // Indicates whether the request was actually sent
// or response returned from ``beforeSend`` event
) {
// Get the response
$response = $event->getResponse();
// Return a new/modified response.
// You can use $event->setResult($response) to achieve the same.
return new \Cake\Http\Client\Response(body: 'something');
}
);
在测试中,您通常希望为外部 API 创建模拟响应。您可以使用 HttpClientTrait
来定义对应用程序正在进行的请求的响应
use Cake\Http\TestSuite\HttpClientTrait;
use Cake\TestSuite\TestCase;
class CartControllerTests extends TestCase
{
use HttpClientTrait;
public function testCheckout()
{
// Mock a POST request that will be made.
$this->mockClientPost(
'https://example.com/process-payment',
$this->newClientResponse(200, [], json_encode(['ok' => true]))
);
$this->post("/cart/checkout");
// Do assertions.
}
}
有一些方法可以模拟最常用的 HTTP 方法
$this->mockClientGet(/* ... */);
$this->mockClientPatch(/* ... */);
$this->mockClientPost(/* ... */);
$this->mockClientPut(/* ... */);
$this->mockClientDelete(/* ... */);
如上所示,您可以使用 newClientResponse()
方法为应用程序将要进行的请求创建响应。头需要是字符串列表
$headers = [
'Content-Type: application/json',
'Connection: close',
];
$response = $this->newClientResponse(200, $headers, $body)