响应组件¶
概览¶
Phalcon\Http\Response是一个封装应用程序向用户实际返回的HTTP响应的组件。最常返回的有效负载是头部和内容。请注意,这并不是唯一的实际响应有效负载。该组件充当响应的构造器,并作为HTTP客户端将响应发送回调用者。only the actual response payload. The component acts as a constructor of the response and as an HTTP client to send the response back to the caller.
<?php
use Phalcon\Http\Response;
// Getting a response instance
$response = new Response();
$response->setStatusCode(404, 'Not Found');
$response->setContent("Sorry, the page doesn't exist");
$response->send();
上述示例演示了我们如何向用户发送404页面。
该组件实现了Phalcon\Http\ResponseInterface, Phalcon\Di\InjectionAware和Phalcon\Events\EventsAware接口。
在实例化时,您可以使用构造函数来设置内容、代码以及状态(如果需要的话)。
<?php
use Phalcon\Http\Response;
// Getting a response instance
$response = new Response(
    "Sorry, the page doesn't exist",
    404, 
    'Not Found'
);
$response->send();
设置完所有必要信息后,我们可以调用send()方法将响应发送回去。然而,在某些情况下,由于错误或应用程序的工作流程,我们的响应可能已经发送回调用者。调用send()将导致屏幕上出现可怕的headers already sent消息。
为了避免这种情况,我们可以使用isSent()方法检查响应是否已将数据发送回调用者。
<?php
use Phalcon\Http\Response;
// Getting a response instance
$response = new Response(
    "Sorry, the page doesn't exist",
    404, 
    'Not Found'
);
if (true !== $response->isSent()) {
    $response->send();
}
获取方法¶
The Phalcon\Http\Response提供多个获取方法,允许您根据应用需求检索有关响应的信息。下列获取方法可用:
| 名称 | 描述 | 
|---|---|
getContent(): string |  返回HTTP响应正文。 | 
getHeaders(): HeadersInterface |  返回包含用户设置的头部的头部对象。 | 
getReasonPhrase(): string|null |  返回原因短语(例如Not Found)。返回的文本是在IANA HTTP状态码文档中指定的。 |  
getStatusCode(): int|null |  返回状态码(例如200)。 |  
内容¶
有多种方法可以用来设置响应的内容或主体。setContent()是使用最频繁的方法。
<?php
use Phalcon\Http\Response;
// Getting a response instance
$response = new Response();
$response->setContent("<h1>Hello World!</h1>");
$response->send();
您还可以与其一起使用setContentLength(),这种方法允许您设置响应的长度或字节数,以及setContentType(),它告诉接收方数据的类型是什么。这种特别方便使用,因为接收方(通常是浏览器)会以不同的方式处理不同类型的响应内容。
注意
所有设置方法都返回响应对象本身,因此它们可以链接起来,提供更流畅的接口。
示例
PDF文件:
<?php
use Phalcon\Http\Response;
$response = new Response();
$contents = file_get_contents('/app/storage/files/invoice.pdf');
$response
    ->setContent($contents)
    ->setContentType('application/pdf')
    ->setHeader(
        'Content-Disposition', 
        "attachment; filename='downloaded.pdf'"
    )
    ->send()
;
JSON:
<?php
use Phalcon\Http\Response;
$response = new Response();
$contents = [
    'invoice' => [
        'id'    => 12345,
        'name'  => 'invoice.pdf',
        'date'  => '2019-01-01 01:02:03',
        'owner' => 'admin',
    ]   
];
$response
    ->setJsonContent($contents)
    ->send();
请注意在上面的JSON示例中,我们使用了setJsonContent()而不是setContent(). setJsonContent()。这使得我们可以将有效负载发送给该方法,并自动将内容类型头设置为application/json并调用json_encode对有效负载进行编码。您还可以将选项和深度作为该方法的最后两个参数传递,这些参数将被json_encode在内部使用:
<?php
use Phalcon\Http\Response;
$response = new Response();
$contents = [
    'invoice' => [
        'id'    => 12345,
        'name'  => 'invoice.pdf',
        'date'  => '2019-01-01 01:02:03',
        'owner' => 'admin',
    ]   
];
$response
    ->setJsonContent($contents, JSON_PRETTY_PRINT, 512)
    ->send();
对于需要基于某些准则(例如各种条件语句)向响应添加内容的应用程序,您可以使用if statements for instance), you can use the appendContent()方法,该方法只会将新内容添加到组件中已存储的内容。
响应头¶
HTTP头部是HTTP响应中非常重要的一部分,因为它们包含了关于响应的信息。诸如状态、内容类型、缓存等信息都被包裹在头部中。Phalcon\Http\Response对象提供了方法,允许您根据应用程序的工作流程和需求操作这些头部。Phalcon\Http\Response object offers methods that allow you to manipulate those headers based on your application workflow and needs.
使用响应对象设置头部只需要调用setHeader()方法设置容器。
<?php
use Phalcon\Http\Response;
$response = new Response();
$response
    ->setHeader(
        'Content-Type', 
        'application/pdf'
    )
    ->setHeader(
        'Content-Disposition', 
        "attachment; filename='downloaded.pdf'"
    )
;
$response->setRawHeader('HTTP/1.1 200 OK');
您还可以使用setRawHeader()方法以原始语法设置头部。
你可以使用以下方法检查头部是否存在hasHeader(),使用以下方法移除它removeHeader()方法,或者完全清除所有头部信息使用resetHeaders().
<?php
use Phalcon\Http\Response;
$response = new Response();
$response->setHeader(
    'Content-Type', 
    'application/pdf'
);
if (true === $response->hasHeader('Content-Type')) {
    $response->removeHeader('Content-Type');
}
$response->resetHeaders();
如果需要,你还可以仅将头部发送回调用方使用sendHeaders()
<?php
use Phalcon\Http\Response;
$response = new Response();
$response->setHeader(
    'Content-Type', 
    'application/pdf'
);
$response->sendHeaders();
The Phalcon\Http\Response对象还封装了Phalcon\Http\Response\Headers集合对象,该对象提供了更多用于头部操作的方法。你可以实例化一个Phalcon\Http\Response\Headers对象或任何实现了Phalcon\Http\Response\HeadersInterface的对象,并通过使用setHeaders():
<?php
use Phalcon\Http\Response;
use Phalcon\Http\Response\Headers;
$response = new Response();
$headers  = new Headers();
$headers
    ->set(
        'Content-Type', 
        'application/pdf'
    )
    ->set(
        'Content-Disposition', 
        "attachment; filename='downloaded.pdf'"
    )
;
$response->setHeaders($headers);
注意
注意,使用setHeaders()将传递的头部与响应对象中已存在的头部进行合并。该方法在设置头部之前不会清除现有头部。要清除头部,你需要先调用reset()(或在响应对象上调用resetHeaders())。
The Phalcon\Http\Response\Headers对象提供了以下方法,允许你操作头部:
| 名称 | 描述 | 
|---|---|
get( string $name ): string|bool |  从对象中获取头部值 | 
has( string $name ): bool |  检查响应中是否已存在某个头部 | 
remove( string $header ) |  从响应中移除某个头部 | 
reset() |  重置所有头部 | 
send(): bool |  将头部发送给客户端 | 
set( string $name, string $value ) |  设置要在响应结束时发送的头部 | 
setRaw( string $header ) |  设置要在响应结束时发送的原始头部 | 
toArray(): array |  以数组形式返回当前的头部 | 
<?php
use Phalcon\Http\Response;
$response = new Response();
$headers  = $response->getHeaders();
$headers->set('Content-Type', 'application/json');
$response->setHeaders($headers);
Cookies¶
The Phalcon\Http\Response提供了一个集合来存储和操作cookie。然后你可以将这些cookie与响应一起发送回去。
要设置cookie,你需要实例化一个Phalcon\Http\Response\Cookies对象或任何实现了Phalcon\Http\Response\CookiesInterface.
<?php
use Phalcon\Http\Response;
use Phalcon\Http\Response\Cookies;
$response = new Response();
$cookies  = new Cookies();
$response->setCookies($cookies);
的对象。要获取用户设置的cookie,你可以使用getCookies()方法在Phalcon\Http\Response对象上。该方法返回一个Phalcon\Http\Response\Cookies集合对象。你可以通过使用setCookies()在响应对象中设置cookie(如上所示),然后使用sendCookies()将它们发送回调用方。
SameSite¶
 如果你使用的是PHP 7.3或更高版本,可以将SameSite作为元素设置到options数组(构造函数的最后一个参数)或通过使用setOptions()。由你负责为SameSite分配一个有效的值(例如Strict, Lax等等)。
<?php
use Phalcon\Http\Cookie;
$cookie  = new Cookie(
    'my-cookie',                   // name
    1234,                          // value
    time() + 86400,                // expires
    "/",                           // path
    true,                          // secure
    ".phalcon.io",                 // domain
    true,                          // httponly
    [                              // options
        "samesite" => "Strict",    // 
    ]                              // 
);
注意
如果你的DI容器包含session服务,则cookie将自动存储在会话中。如果不是,则它们不会被存储,并且如果你希望持久化它们,则需要你自己负责。
加密¶
cookie集合会自动注册为response服务的一部分,该服务在DI容器中注册。默认情况下,在发送cookie到客户端之前会自动加密,并且在从用户检索时会自动解密。
为了设置用于生成消息的签名密钥,你可以在构造函数中设置它:
<?php 
use Phalcon\Http\Response;
use Phalcon\Http\Response\Cookies;
$response = new Response();
$signKey  = "#1dj8$=dp?.ak//j1V$~%*0XaK\xb1\x8d\xa9\x98\x054t7w!z%C*F-Jk\x98\x05\\\x5c";
$cookies  = new Cookies(true, $signKey);
$response->setCookies($cookies);
或者如果你愿意,可以使用setSignKey()方法:
<?php 
use Phalcon\Http\Response;
use Phalcon\Http\Response\Cookies;
$response = new Response();
$signKey  = "#1dj8$=dp?.ak//j1V$~%*0XaK\xb1\x8d\xa9\x98\x054t7w!z%C*F-Jk\x98\x05\\\x5c";
$cookies  = new Cookies();
$cookies->setSignKey($signKey);
$response->setCookies($cookies);
注意
The signKey 必须至少32个字符长,并且如果它是通过密码学安全的伪随机数生成器生成的则总是有帮助的。你总是可以使用Crypt组件生成一个好的signKey.
注意
Cookie可以包含复杂结构,例如服务信息、结果集等。因此,如果没有加密就向客户端发送cookie,可能会暴露应用程序细节,攻击者可能会利用这些细节来危及应用程序和底层系统。如果你不希望使用加密,你可以仅发送唯一标识符,这些标识符可以与数据库表关联,从而存储更复杂的信息供你的应用程序使用。
方法¶
有几个可用的方法可以帮助你从组件中检索数据:
| 方法 | 描述 | 
|---|---|
delete( string $name ): bool |  按名称删除一个cookie。此方法不会从超全局变量中$_COOKIE superglobal |  
get( string $name ): CookieInterface |  按名称获取一个cookie。它检查内部集合,如果找到cookie,则将其返回。如果未找到,则从超全局变量中拾取cookie,创建一个对象,然后返回。它不会将其存储在内部集合中,否则它将被发送两次。 | 
getCookies(): array |  返回对象中所有可用cookie的数组 | 
has( string $name ): bool |  检查内部cookie集合或超全局变量。如果cookie存在于任一集合中,则返回$_COOKIE superglobal. It returns true;否则返回false。 |  
isUsingEncryption(): bool |  返回集合是否正在自动加密/解密cookie。 | 
reset(): CookiesInterface |  从内部集合中重置所有已设置的cookie | 
send(): bool |  将所有cookie发送到客户端。如果在当前请求期间已经发送了头部,则不会发送cookie | 
setSignKey( string $signKey = null ): CookieInterface |  设置cookie的签名密钥。如果设置为NULL则禁用签名。 |  
useEncryption( bool $useEncryption ): CookiesInterface |  设置bag中的cookie必须自动加密/解密 | 
set() |  设置一个将在请求结束时发送的cookie | 
set(): CookiesInterface接受以下参数:
| 参数 | 描述 | 
|---|---|
string $name |  cookie的名称 | 
mixed $value = null |  cookie的值 | 
int $expire = 0 |  cookie的过期时间 | 
string $path = "/" |  cookie的路径 | 
bool $secure = null |  cookie是否安全 | 
string $domain = null |  cookie的域名 | 
bool $httpOnly = false |  是否设置http only | 
<?php
use Phalcon\Http\Response\Cookies;
$now = new DateTimeImmutable();
$tomorrow = $now->modify('tomorrow');
$cookies = new Cookies();
$cookies->set(
    'remember-me',
    json_encode(
        [
            'user_id' => 1,
        ]
    ),
    (int) $tomorrow->format('U')
);
文件¶
The setFileToSend()辅助方法允许你轻松地使用响应对象将文件发送回调用方。这在我们想在应用程序中引入下载文件功能时特别有用。
该方法接受以下参数:
| 参数 | 描述 | 
|---|---|
string $filePath |  文件所在的路径 | 
string $attachmentName |  浏览器将保存文件的名称 | 
bool $attachment |  这是不是一个附件(设置头部) | 
<?php
use Phalcon\Http\Response;
$response = new Response();
$contents = file_get_contents();
$response
    ->setFileToSend(
        '/app/storage/files/invoice.pdf',
        'downloaded.pdf',
        true
    )
    ->send()
;
在上面的例子中,我们设置了文件的位置(/app/storage/files/invoice.pdf)。第二个参数将设置文件名称(当浏览器下载时)为downloaded.pdf。第三个参数指示组件设置相关的下载头部。这些是:
Content-Description: File TransferContent-Type: application/octet-stream"Content-Disposition: attachment; filename=downloaded.pdf;"Content-Transfer-Encoding: binary"
当调用时send(),文件将使用readfile()并将内容发送回调用者。
重定向¶
使用Phalcon\Http\Response还可以执行HTTP重定向。
示例
重定向到默认URI
重定向到posts/index
重定向到外部URI(注意第二个参数设置为true)
<?php 
use Phalcon\Http\Response;
$response = new Response();
$response->redirect('https://en.wikipedia.org', true);
带有HTTP状态码的外部URI重定向,适用于永久或临时重定向。
<?php 
use Phalcon\Http\Response;
$response = new Response();
$response->redirect('https://www.example.com/new-location', true, 301);
所有内部URI都是通过url服务(默认为Phalcon\Mvc\Url)生成的。此示例演示了如何使用在应用程序中定义的路由进行重定向:
<?php 
use Phalcon\Http\Response;
$response = new Response();
return $response->redirect(
    [
        'for'        => 'index-lang',
        'lang'       => 'jp',
        'controller' => 'index',
    ]
);
注意
即使当前动作有一个关联的视图,也不会渲染它,因为redirect禁用了视图。
HTTP缓存¶
提高应用程序性能和减少流量的最简单方法之一是使用HTTP缓存。Phalcon\Http\Response对象提供了有助于完成此任务的方法。
注意
根据您应用程序的需求,您可能不希望使用Phalcon控制HTTP缓存。互联网上有一些可用的服务可以帮助您实现这一点,并且可能会更便宜、更容易维护(如BitMitigate、Varnish等)。在应用程序中实现HTTP缓存肯定会有帮助,但对应用程序性能的影响可能较小。由您决定哪种策略最适合您的应用程序和用户。
HTTP缓存通过在响应中设置某些标头来实现。缓存(使用标头)是在用户首次访问我们的应用程序时设置的。以下标头有助于HTTP缓存:
| 名称 | 描述 | 
|---|---|
Expires: |  设置页面的过期日期。一旦页面过期,浏览器将请求页面的新副本,而不是使用缓存版本。 | 
Cache-Control: |  页面被认为新鲜在浏览器中的时间。 | 
Last-Modified: |  此页面最后一次被应用程序修改的时间(避免重新加载)。 | 
ETag: |  也称为实体标签,它是每个页面的唯一标识符,使用修改时间戳创建。 | 
304: |  发送一个not modified回复 |  
Expires¶
 过期日期是缓存页面在客户端(浏览器)中最简单和最有效的方法之一。从当前日期开始,我们添加页面将在浏览器缓存中存储的时间。在该时间过期之前,浏览器不会请求此页面的副本。
<?php
use Phalcon\Http\Response;
$response   = new Response();
$expiryDate = new DateTime();
$expiryDate->modify('+2 months');
$response->setExpires($expiryDate);
The Phalcon\Http\Response组件会自动将日期格式化为GMT时区,这是在Expires标头中预期的。无论应用程序的时区如何,该组件首先将时间转换为UTC,然后设置Expires标头。将过期日期设置在过去将指示浏览器始终请求页面的新副本。如果我们要强制客户端浏览器请求我们页面的新副本,这特别有用。
<?php
use Phalcon\Http\Response;
$response   = new Response();
$expiryDate = new DateTime();
$expiryDate->modify('-10 minutes');
$response->setExpires($expiryDate);
注意
浏览器依赖于客户端机器的时钟来识别日期是否已过期。因此,这种缓存机制存在一些开发人员必须考虑的限制(不同时区、时钟偏移等)。
Cache-Control¶
 此标头提供了一种更好的方式来缓存提供的页面。我们只需指定秒数,指示浏览器我们的内容在此时间内被缓存。
<?php
use Phalcon\Http\Response;
$response = new Response();
$response->setHeader(
    'Cache-Control', 
    'max-age=86400'
);
如果您不想调用setHeaders(),则可以使用一个实用方法setCache()来为您设置Cache-Control。
<?php
use Phalcon\Http\Response;
$response = new Response();
// $response->setHeader('Cache-Control', 'max-age=86400');
$response->setCache(86400);
为了使上述失效或指示浏览器始终请求页面的新副本,我们可以这样做:
<?php
use Phalcon\Http\Response;
$response = new Response();
$response->setHeader(
    'Cache-Control', 
    'private, max-age=0, must-revalidate'
);
Last-Modified¶
 您还可以使用setLastModified()方法来指示浏览器页面上次修改的时间。此标头不如E-Tag标头准确,但可以用作回退机制。
<?php
use Phalcon\Http\Response;
$response   = new Response();
$expiryDate = new DateTime();
$expiryDate->modify('+2 months');
$response->setLastModified($expiryDate);
The Phalcon\Http\Response组件会自动将日期格式化为GMT时区,这是在一个Last-Modified标头中预期的。无论应用程序的时区如何,该组件首先将时间转换为UTC,然后设置Last-Modified标头。将过期日期设置在过去将指示浏览器始终请求页面的新副本。如果我们要强制客户端浏览器请求我们页面的新副本,这特别有用。
<?php
use Phalcon\Http\Response;
$response   = new Response();
$expiryDate = new DateTime();
$expiryDate->modify('-10 minutes');
$response->setLastModified($expiryDate);
E-Tag¶
 An entity-tag或E-tag是一个唯一标识符,帮助浏览器识别请求之间页面是否已更改。标识符通常是根据最后修改日期、内容和其他识别页面的参数计算得出的:
<?php
use MyApp\Models\Invoices;
use Phalcon\Http\Response;
$response = new Response();
$mostRecentDate = Invoices::maximum(
    [
        'column' => 'inv_created_date',
    ]
);
$eTag = sha1($mostRecentDate);
$response->setHeader('E-Tag', $eTag);
未修改 -304¶
 生成not-modified响应也有助于缓存,指示浏览器内容未被修改,因此应使用浏览器中本地缓存的数据副本。
<?php
use MyApp\Models\Invoices;
use Phalcon\Http\Response;
$response = new Response();
$response->setNotModified();
依赖注入¶
The Phalcon\Http\Response对象实现了Phalcon\Di\InjectionAwareInterface接口。因此,DI容器是可用的,并可以通过getDI()方法检索。也可以通过setDI()方法设置容器。
如果您在应用程序中使用了Phalcon\Di\FactoryDefaultDI容器,则该服务已经为您注册。您可以使用response名称访问它。下面的示例展示了控制器的用法。
<?php
use Phalcon\Http\Response;
use Phalcon\Mvc\Controller;
/**
 * @property Response $response
 */
class PostsController extends Controller
{
    public function uploadAction()
    {
        return $this
            ->response
            ->setStatusCode(404, 'Not Found')
            ->setContent("Sorry, the page does not exist")
            ->send();
    }
}
事件¶
The Phalcon\Http\Response对象实现了Phalcon\Events\EventsAware接口。因此,getEventsManager()和setEventsManager()可供您使用。
| 事件 | 描述 | 可以停止操作 | 
|---|---|---|
afterSendHeaders |  在标头发送后触发 | 否 | 
beforeSendHeaders |  在标头发送前触发 | 是 |