遇到了一个奇怪的 Request 问题

问题:自己创建了 TestRequest 类去继承 Illuminate\Http\Request 在 Controller 中去 use TestRequest
在 Controller 取不到值 下面是代码

TestRequest:

<?php
namespace App\Http\Requests\Course;

use Illuminate\Http\Request;

class TestRequest extends Request
{

}

Controller:

<?php

namespace App\Http\Controllers\Api\Course;

use App\Http\Controllers\Controller;
use App\Http\Requests\Course\TestRequest;

class ElectiveController extends Controller
{

  /**
 * 选修课列表
 * @param TestRequest $request
 */
 public function index(TestRequest $request)
 {  
       $token = $request->get('token');
      dd($request);
  }

}

token 的打印:

null

request 的打印:

遇到了一个奇怪的 Request 问题

我试了一下 直接在 Controller 中继承 Illuminate\Http\Request 是可以取到值的

我的想法是 在初始化的时候有问题 所以里面的都是 null

我也尝试了 直接让 TestReques 继承 Illuminate\Foundation\Http\FormRequest; 是可以取到值的

《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
最佳答案

奥秘在入口文件 public/index.php

// 根据已经注册到容器中的接口获取Http处理核心的实例
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

// 通过Http处理核心的handle处理请求
$response = $kernel->handle(
// 传入请求快照
    $request = Illuminate\Http\Request::capture()
);

// 输出响应
$response->send();

// 响应后续处理,terminate中间件
$kernel->terminate($request, $response);

Illuminate\Http\Request::capture() 方法会调用 SymfonyRequest,获取解析之后的请求对象,大部分请求参数的解析操作在 SymfonyRequest 类里面实现,有兴趣可以查看下 createFromGlobals 方法的实现

public static function capture()
{
    static::enableHttpMethodParameterOverride();

    return static::createFromBase(SymfonyRequest::createFromGlobals());
}

再回到 HttpKernelhandle 方法,这个方法里面就把传入的 request 对象注册到了服务容器中

public function handle($request)
{
    try {
        $request->enableHttpMethodParameterOverride();

        // 这里再次传递了request
        $response = $this->sendRequestThroughRouter($request);
    } catch (Exception $e) {
        $this->reportException($e);

        $response = $this->renderException($request, $e);
    } catch (Throwable $e) {
        $this->reportException($e = new FatalThrowableError($e));

        $response = $this->renderException($request, $e);
    }

    $this->app['events']->dispatch(
        new Events\RequestHandled($request, $response)
    );

    return $response;
}

查看 $this->sendRequestThroughRouter($request) 方法

protected function sendRequestThroughRouter($request)
{
    // 秘密就在这,这里把request实例对象注册到了服务容器的request key上
    $this->app->instance('request', $request);

    Facade::clearResolvedInstance('request');

    $this->bootstrap();

    return (new Pipeline($this->app))
                ->send($request)
                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                ->then($this->dispatchToRouter());
}

那么为什么可以通过 Illuminate\Http\Request 获取到这个 request 对象?
那就需要看一下服务容器是怎么注册 Illuminate\Http\Request 的,这里找了半天没找到相关的代码,然后我就把 dd(app()) 出来看了一下,发现服务容器的 aliasesabstractAliases 有建立 Illuminate\Http\Requestrequest 的映射,然后在服务容器的初始化方法里看到有调用注册 abstractAliases 的方法

// 服务容器构造方法
public function __construct($basePath = null)
{
    if ($basePath) {
        $this->setBasePath($basePath);
    }

    $this->registerBaseBindings();
    $this->registerBaseServiceProviders();
    // 这里注册了aliases
    $this->registerCoreContainerAliases();
}

registerCoreContainerAliases 方法会看到一个二维数组,二维数组里面就定义了一系列的类别名,可以看到这里把 request 作为 Illuminate\Http\RequestSymfony\Component\HttpFoundation\Request 的别名,这样就把这两个类绑定到了 app('request'),进而可以获取到之前注册给 request 键的 request 对象实例

public function registerCoreContainerAliases()
{
    foreach ([
        ...省略其他
        'request'              => [\Illuminate\Http\Request::class, \Symfony\Component\HttpFoundation\Request::class],
        ...省略其他
    ] as $key => $aliases) {
        foreach ($aliases as $alias) {
            $this->alias($key, $alias);
        }
    }
}

现在你就明白了,直接继承 Request 是不行的,你需要通过一种机制去调用 Illuminate\Http\Request::capture(),这个方法返回的才是你所需要的

5年前 评论
讨论数量: 3

奥秘在入口文件 public/index.php

// 根据已经注册到容器中的接口获取Http处理核心的实例
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

// 通过Http处理核心的handle处理请求
$response = $kernel->handle(
// 传入请求快照
    $request = Illuminate\Http\Request::capture()
);

// 输出响应
$response->send();

// 响应后续处理,terminate中间件
$kernel->terminate($request, $response);

Illuminate\Http\Request::capture() 方法会调用 SymfonyRequest,获取解析之后的请求对象,大部分请求参数的解析操作在 SymfonyRequest 类里面实现,有兴趣可以查看下 createFromGlobals 方法的实现

public static function capture()
{
    static::enableHttpMethodParameterOverride();

    return static::createFromBase(SymfonyRequest::createFromGlobals());
}

再回到 HttpKernelhandle 方法,这个方法里面就把传入的 request 对象注册到了服务容器中

public function handle($request)
{
    try {
        $request->enableHttpMethodParameterOverride();

        // 这里再次传递了request
        $response = $this->sendRequestThroughRouter($request);
    } catch (Exception $e) {
        $this->reportException($e);

        $response = $this->renderException($request, $e);
    } catch (Throwable $e) {
        $this->reportException($e = new FatalThrowableError($e));

        $response = $this->renderException($request, $e);
    }

    $this->app['events']->dispatch(
        new Events\RequestHandled($request, $response)
    );

    return $response;
}

查看 $this->sendRequestThroughRouter($request) 方法

protected function sendRequestThroughRouter($request)
{
    // 秘密就在这,这里把request实例对象注册到了服务容器的request key上
    $this->app->instance('request', $request);

    Facade::clearResolvedInstance('request');

    $this->bootstrap();

    return (new Pipeline($this->app))
                ->send($request)
                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                ->then($this->dispatchToRouter());
}

那么为什么可以通过 Illuminate\Http\Request 获取到这个 request 对象?
那就需要看一下服务容器是怎么注册 Illuminate\Http\Request 的,这里找了半天没找到相关的代码,然后我就把 dd(app()) 出来看了一下,发现服务容器的 aliasesabstractAliases 有建立 Illuminate\Http\Requestrequest 的映射,然后在服务容器的初始化方法里看到有调用注册 abstractAliases 的方法

// 服务容器构造方法
public function __construct($basePath = null)
{
    if ($basePath) {
        $this->setBasePath($basePath);
    }

    $this->registerBaseBindings();
    $this->registerBaseServiceProviders();
    // 这里注册了aliases
    $this->registerCoreContainerAliases();
}

registerCoreContainerAliases 方法会看到一个二维数组,二维数组里面就定义了一系列的类别名,可以看到这里把 request 作为 Illuminate\Http\RequestSymfony\Component\HttpFoundation\Request 的别名,这样就把这两个类绑定到了 app('request'),进而可以获取到之前注册给 request 键的 request 对象实例

public function registerCoreContainerAliases()
{
    foreach ([
        ...省略其他
        'request'              => [\Illuminate\Http\Request::class, \Symfony\Component\HttpFoundation\Request::class],
        ...省略其他
    ] as $key => $aliases) {
        foreach ($aliases as $alias) {
            $this->alias($key, $alias);
        }
    }
}

现在你就明白了,直接继承 Request 是不行的,你需要通过一种机制去调用 Illuminate\Http\Request::capture(),这个方法返回的才是你所需要的

5年前 评论

authorize 方法返回 false

5年前 评论
leo
5年前 评论
largezhou 5年前
whcoding (楼主) 5年前

奥秘在入口文件 public/index.php

// 根据已经注册到容器中的接口获取Http处理核心的实例
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

// 通过Http处理核心的handle处理请求
$response = $kernel->handle(
// 传入请求快照
    $request = Illuminate\Http\Request::capture()
);

// 输出响应
$response->send();

// 响应后续处理,terminate中间件
$kernel->terminate($request, $response);

Illuminate\Http\Request::capture() 方法会调用 SymfonyRequest,获取解析之后的请求对象,大部分请求参数的解析操作在 SymfonyRequest 类里面实现,有兴趣可以查看下 createFromGlobals 方法的实现

public static function capture()
{
    static::enableHttpMethodParameterOverride();

    return static::createFromBase(SymfonyRequest::createFromGlobals());
}

再回到 HttpKernelhandle 方法,这个方法里面就把传入的 request 对象注册到了服务容器中

public function handle($request)
{
    try {
        $request->enableHttpMethodParameterOverride();

        // 这里再次传递了request
        $response = $this->sendRequestThroughRouter($request);
    } catch (Exception $e) {
        $this->reportException($e);

        $response = $this->renderException($request, $e);
    } catch (Throwable $e) {
        $this->reportException($e = new FatalThrowableError($e));

        $response = $this->renderException($request, $e);
    }

    $this->app['events']->dispatch(
        new Events\RequestHandled($request, $response)
    );

    return $response;
}

查看 $this->sendRequestThroughRouter($request) 方法

protected function sendRequestThroughRouter($request)
{
    // 秘密就在这,这里把request实例对象注册到了服务容器的request key上
    $this->app->instance('request', $request);

    Facade::clearResolvedInstance('request');

    $this->bootstrap();

    return (new Pipeline($this->app))
                ->send($request)
                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                ->then($this->dispatchToRouter());
}

那么为什么可以通过 Illuminate\Http\Request 获取到这个 request 对象?
那就需要看一下服务容器是怎么注册 Illuminate\Http\Request 的,这里找了半天没找到相关的代码,然后我就把 dd(app()) 出来看了一下,发现服务容器的 aliasesabstractAliases 有建立 Illuminate\Http\Requestrequest 的映射,然后在服务容器的初始化方法里看到有调用注册 abstractAliases 的方法

// 服务容器构造方法
public function __construct($basePath = null)
{
    if ($basePath) {
        $this->setBasePath($basePath);
    }

    $this->registerBaseBindings();
    $this->registerBaseServiceProviders();
    // 这里注册了aliases
    $this->registerCoreContainerAliases();
}

registerCoreContainerAliases 方法会看到一个二维数组,二维数组里面就定义了一系列的类别名,可以看到这里把 request 作为 Illuminate\Http\RequestSymfony\Component\HttpFoundation\Request 的别名,这样就把这两个类绑定到了 app('request'),进而可以获取到之前注册给 request 键的 request 对象实例

public function registerCoreContainerAliases()
{
    foreach ([
        ...省略其他
        'request'              => [\Illuminate\Http\Request::class, \Symfony\Component\HttpFoundation\Request::class],
        ...省略其他
    ] as $key => $aliases) {
        foreach ($aliases as $alias) {
            $this->alias($key, $alias);
        }
    }
}

现在你就明白了,直接继承 Request 是不行的,你需要通过一种机制去调用 Illuminate\Http\Request::capture(),这个方法返回的才是你所需要的

5年前 评论