Laravel core interpretation of Response

不言
Release: 2023-04-02 17:12:01
Original
3054 people have browsed it

This article mainly introduces the core interpretation of Response in Laravel, which has a certain reference value. Now I share it with you. Friends in need can refer to it

Response

We have done in the previous two sections We talked about Laravel's controller and Request object respectively. In the section about the Request object, we looked at how the Request object is created and where the methods it supports are defined. When talking about the controller, we described in detail how Find the controller method corresponding to the Request and then execute the handler. In this section we will talk about the remaining part, how the execution result of the controller method is converted into the response object Response and then returned to the client.

Create Response

Let us return to the code block where Laravel executes the route handler and returns the response:

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

We have already mentioned it in the section about controllers The runRouteWithinStack method is where the routing handler (controller method or closure handler) is finally executed. Through the above code, we can also see that the execution result will be passed to Router The prepareResponse method, when the program flow returns to runRoute, the prepareResponse method is executed again to obtain the Response object to be returned to the client. Let's do this next Take a closer look at the prepareResponse method.

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);
    }
}
Copy after login

In the above code we see three types of Response:

##Class NameRepresentationPsrResponseInterface (alias of PsrHttpMessageResponseInterface)Definition of server response in Psr specificationIlluminateHttpJsonResponse (Subclass of SymfonyComponentHttpFoundationResponse )The definition of server-side JSON response in LaravelIlluminateHttpResponse (a subclass of SymfonyComponentHttpFoundationResponse)The definition of ordinary non-JSON response in Laravel Definition
You can see from the logic in

prepareResponse that no matter what value is returned by the routing execution result, it will eventually be converted into a Response object by Laravel , and these objects are all objects of the SymfonyComponentHttpFoundationResponse class or its subclasses. From here, we can see that like Request, Laravel's Response also relies on the HttpFoundation component of the Symfony framework.

Let’s take a look at the construction method of 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;
    }
}
Copy after login

So the return value of the routing handler will be set to the content attribute of the object when creating the Response object, and the value of this attribute is returned to The response content of the client's response.

Set Response headers

After generating the Response object, the

prepare method of the object must be executed. This method is defined in Symfony\Component\HttpFoundation\ResposneClass, its main purpose is to fine-tune the Response so that it can comply with the HTTP/1.1 protocol (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;
    }
}
Copy after login

prepare sets corresponding response header for various situations, such as Content-Type, Content-LengthWait for these common header fields.

Send Response

After the Response is created and set, it will flow through the post-operations of routing and framework middleware. In the post-operations of the middleware, the Response is generally further processed. , and finally the program flows back to the Http Kernel, and the Http Kernel will send the Response to the client. Let's take a look at this part of the code. The logic of

//入口文件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);
Copy after login
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;
    }
}
Copy after login

send is very easy to understand. Set the previously set headers into the header field of the HTTP response. The Content will be echoed and then set to the main entity of the HTTP response. middle. Finally, PHP will send the complete HTTP response to the client.

After sending the response, Http Kernel will execute the

terminate method to call the terminate method in the terminate middleware, and finally execute the application's termiate method to end The entire application life cycle (from receiving a request to returning a response).

The above is the entire content of this article. I hope it will be helpful to everyone's study. For more related content, please pay attention to the PHP Chinese website!

Related recommendations:

Laravel Core Interpretation Request

##Laravel Core Interpretation Facades

The above is the detailed content of Laravel core interpretation of Response. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!