>  기사  >  백엔드 개발  >  응답의 Laravel 핵심 해석

응답의 Laravel 핵심 해석

不言
不言원래의
2018-07-06 14:56:583033검색

이 글은 주로 Laravel의 Response에 대한 핵심 해석을 소개하고 있는데, 이제 여러분과 공유합니다. 도움이 필요한 친구들이 참고할 수 있습니다.

Response

앞의 두 섹션에서는 Laravel의 컨트롤러와 Request 객체에 대한 섹션에서는 Request 객체가 어떻게 생성되는지, 그리고 그것이 지원하는 메소드가 어디에 정의되어 있는지 살펴보았습니다. 요청한 후 처리를 수행합니다. 이 섹션에서는 나머지 부분, 즉 컨트롤러 메서드의 실행 결과가 응답 객체인 Response로 변환되어 클라이언트에 반환되는 방법에 대해 설명합니다.

Create 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 메소드가 최종 메소드라고 언급했습니다. 라우팅 핸들러(컨트롤러 메소드 또는 클로저 핸들러)가 실행되는 경우, 위의 코드를 통해 실행 결과가 RouterprepareResponse 메소드로 전달되는 것을 볼 수도 있습니다. 프로그램 흐름이 runRoute로 반환되면 prepareResponse 메서드가 다시 실행되어 클라이언트에 반환될 Response 객체를 얻습니다. prepareResponse를 자세히 살펴보겠습니다. 방법. runRouteWithinStack方法里是最终执行路由处理程序(控制器方法或者闭包处理程序)的地方,通过上面的代码我们也可以看到执行的结果会传递给RouterprepareResponse方法,当程序流返回到runRoute里后又执行了一次prepareResponse方法得到了要返回给客户端的Response对象, 下面我们就来详细看一下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);
    }
}

在上面的代码中我们看到有三种Response:

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

通过prepareResponse中的逻辑可以看到,无论路由执行结果返回的是什么值最终都会被Laravel转换为成一个Response对象,而这些对象都是SymfonyComponentHttpFoundationResponse类或者其子类的对象。从这里也就能看出来跟Request一样Laravel的Response也是依赖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属性里,该属性的值就是返回给客户端的响应的响应内容。

设置Response headers

生成Response对象后就要执行对象的prepare方法了,该方法定义在SymfonyComponentHttpFoundationResposne类中,其主要目的是对Response进行微调使其能够遵从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里针对各种情况设置了相应的response header 比如Content-TypeContent-Length等等这些我们常见的首部字段。

发送Response

创建并设置完Response后它会流经路由和框架中间件的后置操作,在中间件的后置操作里一般都是对Response进行进一步加工,最后程序流回到Http Kernel那里, Http Kernel会把Response发送给客户端,我们来看一下这部分的代码。

//入口文件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的逻辑就非常好理解了,把之前设置好的那些headers设置到HTTP响应的首部字段里,Content会echo后被设置到HTTP响应的主体实体中。最后PHP会把完整的HTTP响应发送给客户端。

send响应后Http Kernel会执行terminate方法调用terminate中间件里的terminate方法,最后执行应用的termiaterrreee

위 코드에는 세 가지 유형의 응답이 있음을 알 수 있습니다.

클래스 이름 표현
PsrResponseInterface(PsrHttpMessageResponseInterface의 별칭) Psr 사양의 서버 응답 정의
IlluminateHttpJsonResponse(SymfonyComponentHttpFoundationResponse의 하위 클래스) Laravel의 서버측 JSON 응답 정의
IlluminateHttpResponse(SymfonyComponentHttpFoundationResponse의 하위 클래스) Laravel의 일반적인 비JSON 응답 정의
라우트 실행 결과에 관계없이 prepareResponse의 로직을 통해 확인할 수 있습니다. 반환된 값은 결국 Laravel에 의해 Response 객체로 변환되며, 이러한 객체는 모두 SymfonyComponentHttpFoundationResponse 클래스 또는 그 하위 클래스의 객체입니다. 여기에서 Request와 마찬가지로 Laravel의 Response도 Symfony 프레임워크의 HttpFoundation 구성 요소에 의존한다는 것을 알 수 있습니다.

SymfonyComponentHttpFoundationResponse의 생성 방법을 살펴보겠습니다.

rrreee그래서 Response 객체 생성 시 라우트 핸들러의 반환 값은 객체의 content 속성으로 설정됩니다. 이 속성의 값은 의 응답 내용입니다. 클라이언트에 응답이 반환되었습니다.
응답 헤더 설정

응답 보내기🎜🎜응답이 생성되고 설정된 후에는 라우팅 및 프레임워크 미들웨어의 사후 작업을 통해 흐르게 되며 미들웨어의 사후 작업에서는 일반적으로 응답이 추가 처리되고 최종적으로 프로그램이 진행됩니다. HTTP 커널로 다시 흐릅니다. 여기서 Http 커널은 클라이언트에 응답을 보냅니다. 코드의 이 부분을 살펴보겠습니다. 🎜rrreeerrreee🎜send의 논리는 이해하기 매우 쉽습니다. 이전에 설정된 헤더를 HTTP 응답의 헤더 필드에 설정하면 콘텐츠가 에코되어 HTTP 응답의 기본 엔터티로 설정됩니다. 마지막으로 PHP는 완전한 HTTP 응답을 클라이언트에 보냅니다. 🎜🎜응답을 보낸 후 HTTP 커널은 terminate 메서드를 실행하여 종료 미들웨어의 terminate 메서드를 호출하고 마지막으로 애플리케이션의 termiate를 실행합니다. > 전체 프로세스를 종료하는 방법입니다(요청 수신부터 응답 반환까지). 🎜🎜위 내용은 이 글의 전체 내용입니다. 모든 분들의 학습에 도움이 되었으면 좋겠습니다. 더 많은 관련 내용은 PHP 중국어 홈페이지를 주목해주세요! 🎜🎜관련 권장사항: 🎜🎜🎜Laravel Core Interpretation Request🎜🎜🎜🎜🎜Laravel Core Interpretation Facades🎜🎜🎜

위 내용은 응답의 Laravel 핵심 해석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.