Maison  >  Article  >  développement back-end  >  Interprétation de base de Laravel de la réponse

Interprétation de base de Laravel de la réponse

不言
不言original
2018-07-06 14:56:583033parcourir

Cet article présente principalement l'interprétation de base de Laravel Response, qui a une certaine valeur de référence. Maintenant, je le partage avec vous. Les amis dans le besoin peuvent s'y référer

Réponse

Nous avons. nous l'avons fait dans les deux sections précédentes. Nous avons parlé respectivement du contrôleur de Laravel et de l'objet Request. Dans la section sur l'objet Request, nous avons examiné comment l'objet Request est créé et où sont définies les méthodes qu'il prend en charge. décrit en détail comment trouver la méthode du contrôleur correspondant à la requête, puis exécuter le gestionnaire. Dans cette section, nous parlerons de la partie restante, comment le résultat de l'exécution de la méthode du contrôleur est converti en objet de réponse Response puis renvoyé au. client.

Créer une réponse

Retournons au bloc de code où Laravel exécute le gestionnaire de route et renvoie la réponse :

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()
                        );
                    });
    
    }
}

Nous l'avons déjà mentionné dans la section sur les contrôleurs Nous sommes allés à la méthode runRouteWithinStack où le gestionnaire de routage (méthode du contrôleur ou gestionnaire de fermeture) est finalement exécuté. Grâce au code ci-dessus, nous pouvons également voir que le résultat de l'exécution sera transmis à la méthode Router de prepareResponse. . Lorsque le flux du programme revient à runRoute, la méthode prepareResponse est à nouveau exécutée pour obtenir l'objet Response à renvoyer au client. Examinons en détail la méthode 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);
    }
}

Dans le code ci-dessus, nous voyons qu'il existe trois types de réponse :

Class Name Representation
PsrResponseInterface(PsrHttpMessageResponseInterface的别名) Psr规范中对服务端响应的定义
IlluminateHttpJsonResponse (SymfonyComponentHttpFoundationResponse的子类) Laravel中对服务端JSON响应的定义
IlluminateHttpResponse (SymfonyComponentHttpFoundationResponse的子类) Laravel中对普通的非JSON响应的定义

Comme vous pouvez le voir d'après la logique dans prepareResponse, quelle que soit la valeur renvoyée à la suite de l'exécution du routage, il sera finalement converti en objet Response par Laravel, et ces objets sont tous des objets de la classe SymfonyComponentHttpFoundationResponse ou de ses sous-classes. De là, nous pouvons voir que comme Request, Laravel's Response s'appuie également sur le composant HttpFoundation du framework Symfony.

Jetons un coup d'œil à la méthode de construction de 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;
    }
}

Ainsi, la valeur de retour du gestionnaire de route sera définie sur l'attribut content de l'objet lors de la création de l'objet Response, et la valeur de cet attribut est le contenu de la réponse renvoyée au client.

Définir les en-têtes de réponse

Après avoir généré l'objet Response, vous devez exécuter la méthode prepare de l'objet. Cette méthode est définie dans la classe SymfonyComponentHttpFoundationResposne. Son objectif principal est d'affiner. ajustez la réponse afin qu'elle puisse être conforme au protocole 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 a un ensemble response header correspondant pour diverses situations, telles que Content-Type, Content-Length, etc. Ce sont nos champs d'en-tête communs.

Envoyer la réponse

Une fois la réponse créée et configurée, elle passera par les post-opérations du middleware de routage et du framework. Dans les post-opérations du middleware, la réponse est généralement. est ensuite traité. , et enfin le programme retourne au noyau Http, et le noyau Http enverra la réponse au client. Jetons un coup d'œil à cette partie du code. La logique de

//入口文件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 est très facile à comprendre. Définissez les en-têtes précédemment définis dans le champ d'en-tête de la réponse HTTP, puis défini dans le corps de. la réponse HTTP dans l’entité. Enfin, PHP enverra la réponse HTTP complète au client.

Après avoir envoyé la réponse, Http Kernel exécutera la méthode terminate pour appeler la méthode terminate dans le middleware de fin, et enfin exécutera la méthode termiate de l'application pour terminer tout le cycle de vie de l'application (de réception de la demande jusqu'au retour de la réponse Terminer).

Ce qui précède représente l'intégralité du contenu de cet article. J'espère qu'il sera utile à l'étude de chacun. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois !

Recommandations associées :

Demande d'interprétation de Laravel Core

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn