ホームページ  >  記事  >  バックエンド開発  >  Response の Laravel コア解釈

Response の Laravel コア解釈

不言
不言オリジナル
2018-07-06 14:56:583105ブラウズ

この記事では、Laravel における Response のコアな解釈を中心に紹介しますが、これは一定の参考値がありますので、共有します。必要な友人は参考にしてください。

Response

We前の 2 つのセクションで説明したとおり、Laravel のコントローラーと Request オブジェクトについてそれぞれ説明しました。Request オブジェクトに関するセクションでは、Request オブジェクトがどのように作成されるか、およびそれがサポートするメソッドがどこで定義されているかを説明しました。コントローラーについて説明するときは、 Request に対応するコントローラー メソッドを検索してハンドラーを実行する方法について詳しく説明しましたが、このセクションでは残りの部分、コントローラー メソッドの実行結果が応答オブジェクト Response に変換されてクライアントに返される方法について説明します。 。

レスポンスの作成

Laravel がルート ハンドラーを実行してレスポンスを返すコード ブロックに戻りましょう:

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

        $this->events->dispatch(new Events\RouteMatched($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;
        //收集路由和控制器里应用的中间件
        $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()
                        );
                    });
    
    }
}

これについてはコントローラーに関するセクションですでに説明しました。 runRouteWithinStack メソッドは、ルーティング ハンドラー (コントローラー メソッドまたはクロージャー ハンドラー) が最終的に実行される場所です。上記のコードを通じて、実行結果が Router に渡されることもわかります。 ##prepareResponse メソッド、プログラム フローが runRoute に戻ると、クライアントに返される Response オブジェクトを取得するために prepareResponse メソッドが再度実行されます。次にこれを実行しましょう。 prepareResponse メソッドを詳しく見てみましょう。

class Router implements RegistrarContract, BindingRegistrar
{
    /**
     * 通过给定值创建Response对象
     *
     * @param  \Symfony\Component\HttpFoundation\Request  $request
     * @param  mixed  $response
     * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
     */
    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);
    }
}

上記のコードでは、次の 3 種類の応答が表示されます。

##クラス名#PsrResponseInterface (PsrHttpMessageResponseInterface のエイリアス)Psr 仕様におけるサーバー応答の定義IlluminateHttpJsonResponse (SymfonyComponentHttpFoundationResponse のサブクラス)Laravel でのサーバーサイド JSON レスポンスの定義IlluminateHttpResponse (SymfonyComponentHttpFoundationResponse のサブクラス)Laravel での通常の非 JSON レスポンスの定義prepareResponse
表現
のロジックから、ルーティングの実行結果がどのような値を返しても、最終的には Response オブジェクトに変換されることがわかります。 Laravel 、およびこれらのオブジェクトはすべて SymfonyComponentHttpFoundationResponse クラスまたはそのサブクラスのオブジェクトです。ここから、リクエストと同様に、Laravel のレスポンスも Symfony フレームワークの

HttpFoundation コンポーネントに依存していることがわかります。 SymfonyComponentHttpFoundationResponse の構築メソッドを見てみましょう:

namespace Symfony\Component\HttpFoundation;
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');
    }
    //设置响应的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;
    }
}

したがって、ルーティング ハンドラーの戻り値は、Response オブジェクトの作成時にオブジェクトの content 属性に設定され、この属性の値は、クライアントの応答の応答コンテンツに返されます。

Set Response headers

Response オブジェクトを生成した後、オブジェクトの

prepare

メソッドを実行する必要があります。このメソッドは

Symfony\Component\HttpFoundation で定義されています\ResposneClass の主な目的は、HTTP/1.1 プロトコル (RFC 2616) に準拠できるように応答を微調整することです。

namespace Symfony\Component\HttpFoundation;
class Response
{
    //在响应被发送给客户端之前对其进行修订使其能遵从HTTP/1.1协议
    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

は、

Content-TypeContent-Length## など、さまざまな状況に対応する 応答ヘッダー を設定します #Waitこれらの一般的なヘッダー フィールドの場合。 応答の送信応答が作成および設定された後、応答はルーティングおよびフレームワーク ミドルウェアの後処理を通過します。ミドルウェアの後処理では、通常、応答はさらに続きます。処理され、最後にプログラムは HTTP カーネルに戻り、HTTP カーネルはクライアントに応答を送信します。コードのこの部分を見てみましょう。

//入口文件public/index.php
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

$response->send();

$kernel->terminate($request, $response);
namespace Symfony\Component\HttpFoundation;
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;
    }
    
    //发送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;
    }
    
    //发送响应内容到客户端
    public function sendContent()
    {
        echo $this->content;

        return $this;
    }
}

send

のロジックは非常に分かりやすく、HTTP レスポンスのヘッダーフィールドにあらかじめ設定したヘッダーを設定すると、Content がエコーされて本体に設定されます。 HTTP レスポンス。最後に、PHP は完全な HTTP 応答をクライアントに送信します。

応答を送信した後、HTTP カーネルは terminate メソッドを実行して終了ミドルウェアの

terminate

メソッドを呼び出し、最後にアプリケーションの termiate# を実行します。 ## 終了するメソッド アプリケーションのライフサイクル全体 (リクエストの受信からレスポンスの返しまで)。 上記がこの記事の全内容です。皆様の学習に少しでもお役に立てれば幸いです。その他の関連コンテンツについては、PHP 中国語 Web サイトをご覧ください。 関連する推奨事項:

Laravel コア解釈リクエスト

#Laravel コア解釈ファサード

以上がResponse の Laravel コア解釈の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。