国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Laravel核心解讀 -- Response

TigerChain / 3399人閱讀

摘要:設(shè)置生成對象后就要執(zhí)行對象的方法了,該方法定義在類中,其主要目的是對進(jìn)行微調(diào)使其能夠遵從協(xié)議。最后會(huì)把完整的響應(yīng)發(fā)送給客戶端。本文已經(jīng)收錄在系列文章源碼學(xué)習(xí)里,歡迎訪問閱讀。

Response

前面兩節(jié)我們分別講了Laravel的控制器和Request對象,在講Request對象的那一節(jié)我們看了Request對象是如何被創(chuàng)建出來的以及它支持的方法都定義在哪里,講控制器時(shí)我們詳細(xì)地描述了如何找到Request對應(yīng)的控制器方法然后執(zhí)行處理程序的,本節(jié)我們就來說剩下的那一部分,控制器方法的執(zhí)行結(jié)果是如何被轉(zhuǎn)換成響應(yīng)對象Response然后返回給客戶端的。

創(chuàng)建Response

讓我們回到Laravel執(zhí)行路由處理程序返回響應(yīng)的代碼塊:

namespace IlluminateRouting;
class Router implements RegistrarContract, BindingRegistrar
{     
    protected function runRoute(Request $request, Route $route)
    {
        $request->setRouteResolver(function () use ($route) {
            return $route;
        });

        $this->events->dispatch(new EventsRouteMatched($route, $request));

        return $this->prepareResponse($request,
            $this->runRouteWithinStack($route, $request)
        );
    }
    
    protected function runRouteWithinStack(Route $route, Request $request)
    {
        $shouldSkipMiddleware = $this->container->bound("middleware.disable") &&
                            $this->container->make("middleware.disable") === true;
        //收集路由和控制器里應(yīng)用的中間件
        $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);

        return (new Pipeline($this->container))
                    ->send($request)
                    ->through($middleware)
                    ->then(function ($request) use ($route) {
                        return $this->prepareResponse(
                            $request, $route->run()
                        );
                    });
    
    }
}

在講控制器的那一節(jié)里我們已經(jīng)提到過runRouteWithinStack方法里是最終執(zhí)行路由處理程序(控制器方法或者閉包處理程序)的地方,通過上面的代碼我們也可以看到執(zhí)行的結(jié)果會(huì)傳遞給RouterprepareResponse方法,當(dāng)程序流返回到runRoute里后又執(zhí)行了一次prepareResponse方法得到了要返回給客戶端的Response對象, 下面我們就來詳細(xì)看一下prepareResponse方法。

class Router implements RegistrarContract, BindingRegistrar
{
    /**
     * 通過給定值創(chuàng)建Response對象
     *
     * @param  SymfonyComponentHttpFoundationRequest  $request
     * @param  mixed  $response
     * @return IlluminateHttpResponse|IlluminateHttpJsonResponse
     */
    public function prepareResponse($request, $response)
    {
        return static::toResponse($request, $response);
    }
    
    public static function toResponse($request, $response)
    {
        if ($response instanceof Responsable) {
            $response = $response->toResponse($request);
        }

        if ($response instanceof PsrResponseInterface) {
            $response = (new HttpFoundationFactory)->createResponse($response);
        } elseif (! $response instanceof SymfonyResponse &&
                   ($response instanceof Arrayable ||
                    $response instanceof Jsonable ||
                    $response instanceof ArrayObject ||
                    $response instanceof JsonSerializable ||
                    is_array($response))) {
            $response = new JsonResponse($response);
        } elseif (! $response instanceof SymfonyResponse) {
            $response = new Response($response);
        }

        if ($response->getStatusCode() === Response::HTTP_NOT_MODIFIED) {
            $response->setNotModified();
        }

        return $response->prepare($request);
    }
}

在上面的代碼中我們看到有三種Response:

Class Name Representation
PsrResponseInterface(PsrHttpMessageResponseInterface的別名) Psr規(guī)范中對服務(wù)端響應(yīng)的定義
IlluminateHttpJsonResponse (SymfonyComponentHttpFoundationResponse的子類) Laravel中對服務(wù)端JSON響應(yīng)的定義
IlluminateHttpResponse (SymfonyComponentHttpFoundationResponse的子類) Laravel中對普通的非JSON響應(yīng)的定義

通過prepareResponse中的邏輯可以看到,無論路由執(zhí)行結(jié)果返回的是什么值最終都會(huì)被Laravel轉(zhuǎn)換為成一個(gè)Response對象,而這些對象都是SymfonyComponentHttpFoundationResponse類或者其子類的對象。從這里也就能看出來跟Request一樣Laravel的Response也是依賴Symfony框架的HttpFoundation組件來實(shí)現(xiàn)的。

我們來看一下SymfonyComponentHttpFoundationResponse的構(gòu)造方法:

namespace SymfonyComponentHttpFoundation;
class Response
{
    public function __construct($content = "", $status = 200, $headers = array())
    {
        $this->headers = new ResponseHeaderBag($headers);
        $this->setContent($content);
        $this->setStatusCode($status);
        $this->setProtocolVersion("1.0");
    }
    //設(shè)置響應(yīng)的Content
    public function setContent($content)
    {
        if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable(array($content, "__toString"))) {
            throw new UnexpectedValueException(sprintf("The Response content must be a string or object implementing __toString(), "%s" given.", gettype($content)));
        }

        $this->content = (string) $content;

        return $this;
    }
}

所以路由處理程序的返回值在創(chuàng)業(yè)Response對象時(shí)會(huì)設(shè)置到對象的content屬性里,該屬性的值就是返回給客戶端的響應(yīng)的響應(yīng)內(nèi)容。

設(shè)置Response headers

生成Response對象后就要執(zhí)行對象的prepare方法了,該方法定義在SymfonyComponentHttpFoundationResposne類中,其主要目的是對Response進(jìn)行微調(diào)使其能夠遵從HTTP/1.1協(xié)議(RFC 2616)。

namespace SymfonyComponentHttpFoundation;
class Response
{
    //在響應(yīng)被發(fā)送給客戶端之前對其進(jìn)行修訂使其能遵從HTTP/1.1協(xié)議
    public function prepare(Request $request)
    {
        $headers = $this->headers;

        if ($this->isInformational() || $this->isEmpty()) {
            $this->setContent(null);
            $headers->remove("Content-Type");
            $headers->remove("Content-Length");
        } else {
            // Content-type based on the Request
            if (!$headers->has("Content-Type")) {
                $format = $request->getRequestFormat();
                if (null !== $format && $mimeType = $request->getMimeType($format)) {
                    $headers->set("Content-Type", $mimeType);
                }
            }

            // Fix Content-Type
            $charset = $this->charset ?: "UTF-8";
            if (!$headers->has("Content-Type")) {
                $headers->set("Content-Type", "text/html; charset=".$charset);
            } elseif (0 === stripos($headers->get("Content-Type"), "text/") && false === stripos($headers->get("Content-Type"), "charset")) {
                // add the charset
                $headers->set("Content-Type", $headers->get("Content-Type")."; charset=".$charset);
            }

            // Fix Content-Length
            if ($headers->has("Transfer-Encoding")) {
                $headers->remove("Content-Length");
            }

            if ($request->isMethod("HEAD")) {
                // cf. RFC2616 14.13
                $length = $headers->get("Content-Length");
                $this->setContent(null);
                if ($length) {
                    $headers->set("Content-Length", $length);
                }
            }
        }

        // Fix protocol
        if ("HTTP/1.0" != $request->server->get("SERVER_PROTOCOL")) {
            $this->setProtocolVersion("1.1");
        }

        // Check if we need to send extra expire info headers
        if ("1.0" == $this->getProtocolVersion() && false !== strpos($this->headers->get("Cache-Control"), "no-cache")) {
            $this->headers->set("pragma", "no-cache");
            $this->headers->set("expires", -1);
        }

        $this->ensureIEOverSSLCompatibility($request);

        return $this;
    }
}

prepare里針對各種情況設(shè)置了相應(yīng)的response header 比如Content-TypeContent-Length等等這些我們常見的首部字段。

發(fā)送Response

創(chuàng)建并設(shè)置完Response后它會(huì)流經(jīng)路由和框架中間件的后置操作,在中間件的后置操作里一般都是對Response進(jìn)行進(jìn)一步加工,最后程序流回到Http Kernel那里, Http Kernel會(huì)把Response發(fā)送給客戶端,我們來看一下這部分的代碼。

//入口文件public/index.php
$kernel = $app->make(IlluminateContractsHttpKernel::class);

$response = $kernel->handle(
    $request = IlluminateHttpRequest::capture()
);

$response->send();

$kernel->terminate($request, $response);
namespace SymfonyComponentHttpFoundation;
class Response
{
    public function send()
    {
        $this->sendHeaders();
        $this->sendContent();

        if (function_exists("fastcgi_finish_request")) {
            fastcgi_finish_request();
        } elseif ("cli" !== PHP_SAPI) {
            static::closeOutputBuffers(0, true);
        }

        return $this;
    }
    
    //發(fā)送headers到客戶端
    public function sendHeaders()
    {
        // headers have already been sent by the developer
        if (headers_sent()) {
            return $this;
        }

        // headers
        foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) {
            foreach ($values as $value) {
                header($name.": ".$value, false, $this->statusCode);
            }
        }

        // status
        header(sprintf("HTTP/%s %s %s", $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);

        // cookies
        foreach ($this->headers->getCookies() as $cookie) {
            if ($cookie->isRaw()) {
                setrawcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly());
            } else {
                setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly());
            }
        }

        return $this;
    }
    
    //發(fā)送響應(yīng)內(nèi)容到客戶端
    public function sendContent()
    {
        echo $this->content;

        return $this;
    }
}

send的邏輯就非常好理解了,把之前設(shè)置好的那些headers設(shè)置到HTTP響應(yīng)的首部字段里,Content會(huì)echo后被設(shè)置到HTTP響應(yīng)的主體實(shí)體中。最后PHP會(huì)把完整的HTTP響應(yīng)發(fā)送給客戶端。

send響應(yīng)后Http Kernel會(huì)執(zhí)行terminate方法調(diào)用terminate中間件里的terminate方法,最后執(zhí)行應(yīng)用的termiate方法來結(jié)束整個(gè)應(yīng)用生命周期(從接收請求開始到返回響應(yīng)結(jié)束)。

本文已經(jīng)收錄在系列文章Laravel源碼學(xué)習(xí)里,歡迎訪問閱讀。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/28711.html

相關(guān)文章

  • Laravel核心解讀 -- 事件系統(tǒng)

    摘要:對于包含通配符的事件名,會(huì)被統(tǒng)一放入數(shù)組中,是用來創(chuàng)建事件對應(yīng)的的如果是監(jiān)聽器是類,去創(chuàng)建監(jiān)聽類創(chuàng)建的時(shí)候,會(huì)判斷監(jiān)聽對象是監(jiān)聽類還是閉包函數(shù)。對于閉包監(jiān)聽來說,會(huì)再包裝一層返回一個(gè)閉包函數(shù)作為事件的監(jiān)聽者。 事件系統(tǒng) Laravel 的事件提供了一個(gè)簡單的觀察者實(shí)現(xiàn),能夠訂閱和監(jiān)聽?wèi)?yīng)用中發(fā)生的各種事件。事件機(jī)制是一種很好的應(yīng)用解耦方式,因?yàn)橐粋€(gè)事件可以擁有多個(gè)互不依賴的監(jiān)聽器。lar...

    chengjianhua 評論0 收藏0
  • Laravel核心解讀 -- 擴(kuò)展用戶認(rèn)證系統(tǒng)

    摘要:擴(kuò)展用戶認(rèn)證系統(tǒng)上一節(jié)我們介紹了系統(tǒng)實(shí)現(xiàn)的一些細(xì)節(jié)知道了是如何應(yīng)用看守器和用戶提供器來進(jìn)行用戶認(rèn)證的,但是針對我們自己開發(fā)的項(xiàng)目或多或少地我們都會(huì)需要在自帶的看守器和用戶提供器基礎(chǔ)之上做一些定制化來適應(yīng)項(xiàng)目,本節(jié)我會(huì)列舉一個(gè)在做項(xiàng)目時(shí)遇到的 擴(kuò)展用戶認(rèn)證系統(tǒng) 上一節(jié)我們介紹了Laravel Auth系統(tǒng)實(shí)現(xiàn)的一些細(xì)節(jié)知道了Laravel是如何應(yīng)用看守器和用戶提供器來進(jìn)行用戶認(rèn)證的,但是...

    王偉廷 評論0 收藏0
  • Laravel核心解讀--中間件(Middleware)

    摘要:解析出后將進(jìn)入應(yīng)用的請求對象傳遞給的方法,在方法負(fù)責(zé)處理流入應(yīng)用的請求對象并返回響應(yīng)對象。攜帶了本次迭代的值。通過這種方式讓請求對象依次流過了要通過的中間件,達(dá)到目的地的方法。 中間件(Middleware)在Laravel中起著過濾進(jìn)入應(yīng)用的HTTP請求對象(Request)和完善離開應(yīng)用的HTTP響應(yīng)對象(Reponse)的作用, 而且可以通過應(yīng)用多個(gè)中間件來層層過濾請求、逐步完善...

    enda 評論0 收藏0
  • Laravel核心解讀--完結(jié)篇

    摘要:過去一年時(shí)間寫了多篇文章來探討了我認(rèn)為的框架最核心部分的設(shè)計(jì)思路代碼實(shí)現(xiàn)。為了大家閱讀方便,我把這些源碼學(xué)習(xí)的文章匯總到這里。數(shù)據(jù)庫算法和數(shù)據(jù)結(jié)構(gòu)這些都是編程的內(nèi)功,只有內(nèi)功深厚了才能解決遇到的復(fù)雜問題。 過去一年時(shí)間寫了20多篇文章來探討了我認(rèn)為的Larave框架最核心部分的設(shè)計(jì)思路、代碼實(shí)現(xiàn)。通過更新文章自己在軟件設(shè)計(jì)、文字表達(dá)方面都有所提高,在剛開始決定寫Laravel源碼分析地...

    laoLiueizo 評論0 收藏0
  • Laravel核心解讀--服務(wù)提供器(ServiceProvider)

    摘要:調(diào)用了的可以看出,所有服務(wù)提供器都在配置文件文件的數(shù)組中。啟動(dòng)的啟動(dòng)由類負(fù)責(zé)引導(dǎo)應(yīng)用的屬性中記錄的所有服務(wù)提供器,就是依次調(diào)用這些服務(wù)提供器的方法,引導(dǎo)完成后就代表應(yīng)用正式啟動(dòng)了,可以開始處理請求了。 服務(wù)提供器是所有 Laravel 應(yīng)用程序引導(dǎo)中心。你的應(yīng)用程序自定義的服務(wù)、第三方資源包提供的服務(wù)以及 Laravel 的所有核心服務(wù)都是通過服務(wù)提供器進(jìn)行注冊(register)和引...

    Richard_Gao 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<