【问题标题】:How to prevent a wrong uuid with model injection binding?如何使用模型注入绑定防止错误的 uuid?
【发布时间】:2023-03-05 14:02:01
【问题描述】:

使用 Laravel 6,我在控制器中注入了一个模型,如下所示:

public function edit(School $school)
{
    return view('/school/form', ['school' => $school]);
}

当我输入这样的网址时,一切正常:

http://localhost:8000/schools/3d537a0f-4c74-4fae-99af-6f1b2c4b34c8/edit

但是如果我尝试另一个像这样的网址(使用错误的 uuid):

http://localhost:8000/schools/3d537a0f-4c74-4fae-99af-6f/edit

然后我有这个 PostgreSQL 错误:

SQLSTATE[22P02]:无效的文本表示:7 错误:无效 uuid 类型的输入语法:“3d537a0f-4c74-4fae-99af-6f1b2c4b34c” (SQL: select * from "schools" where "id" = 3d537a0f-4c74-4fae-99af-6f1b2c4b34c 限制 1)

这是否意味着我必须先检查 uuid ?有什么优雅的方法可以防止这种情况并自动重定向到 404 页面?

【问题讨论】:

  • 感谢您提供此链接。我认为我的模型配置良好: public $incrementing = false;受保护的 $keyType = '字符串';所以我认为问题不在于模型。
  • 我参考了问题的解决方案:我删除了 storage/framework/sessions 里面的会话缓存文件,然后全部恢复工作。我建议清除缓存完全虽然
  • 我试图清除缓存,但没有成功。我继续搜索。

标签: php laravel postgresql


【解决方案1】:

在你的路由定义中,你可以对参数使用一个约束来避免匹配:

Route::get('school/{school}/edit', 'SchoolController@edit')
    ->where('school', '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$');

这将导致 404,因为路由不匹配无效的 UUID。可以在in the documentation 找到有关此主题的更多信息。还有一个解释如何定义一个全局约束以避免对每条路由重复。


对于资源路由,解决方案略有不同。您需要在 RouteServiceProviderboot() 方法中定义一个全局约束:

Route::pattern('school', '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$');

如果你的资源调用方式与你的路由参数不同,你需要在定义路由资源时自定义参数名称:

Route::resource('university', 'UniversityController', [
    'parameters' => ['university' => 'school']
]);

注意:这只是为了说明,我怀疑你需要它......

【讨论】:

  • 嗨纳莫舍克。我试过你的解决方案,但我使用这样的资源路由:Route::resources([ 'schools' => 'SchoolController', ])->where('school', '^[0-9a-f]{8} -[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$');它不起作用。也许我必须编写单独的路线?
  • @Dom 我添加了路由资源的解决方案。
  • 嗨。它就是这样工作的。谢谢。但这似乎有点复杂。也许另一种解决方案可能是使用字符串而不是 uuid 作为表的 id,即使它是存储在此列中的 uuid?你怎么看?多姆
  • 如果你使用不同于主键的东西作为路由参数,你需要使用@cbaconnier提供的解决方案。而且因为它不是真的更简洁......这并不重要。 ;)
  • 当你使用模式并传入不同的数据类型时,Laravel 返回一个 html 404。当找不到路由模型时,是否有一种类似于使用 ->missing() 的简单方法来返回 json?
【解决方案2】:

我相信您在其他地方遇到了以某种方式干扰模型分辨率的问题,应该更深入地了解原因(也许像一个包?)。

作为一种解决方法,您可以使用显式绑定来防止此错误

在您的 RouteServiceProvider.php 文件中

use Ramsey\Uuid\Uuid;

...

public function boot()
{
    parent::boot();

    Route::bind('school', function ($value) {
        if (! Uuid::isValid($value)) { 
            throw (new ModelNotFoundException)->setModel(School::class, $value);
        }

        return School::findOrFail($value);
    });
}

但是,这是一个 hack,只是为了修补一个不应该存在的错误。

【讨论】:

  • 您的解决方案没有您描述的那么糟糕。使用Route::pattern() 时的内部逻辑有些相似。只要您可以访问'school' 参数,您也可以只添加一个全局约束。它会抛出 404 而不是 ModelNotFoundException
  • 我所做的是模仿laravel“成功”找不到模型时的行为。在我看来,这种补丁只有在我们无法解决根本问题时才应该进行。这可能是 laravel 6 和 postgresql 引入的错误
  • 不行,只是在使用路由资源的时候不能添加本地路由绑定约束。如我的回答所示,您仍然可以使用全局约束。
  • 你也不应该需要约束。 Laravel 返回 ModelNotFoundException (这是一个 404 btw),带有有效或无效的 UUID,因为带有 ['uuid' => 'MY-INVALID-UUID'] 的模型不存在。
  • 通常是的,但根据问题,postgresql 服务器无法处理无效 UUID 的字符串。也许框架甚至不应该允许这样的数据库调用,但最简单的解决方案是不要路由带有无效 UUID 的调用(从安全角度来看这也是一件好事)。
【解决方案3】:

是的,请检查id是否退出然后返回数据,否则重定向到404。

或者你可以使用 Eloquent 的 findOrFail 方法。示例:

$model = App\Flight::findOrFail(1);

如果找不到记录,这将自动重定向到 404。

【讨论】:

  • 嗨 ashish bansal。感谢您的回复。是的,它会像你写的那样工作。但是用这种方法我不得不忘记“模型注入”。而且我喜欢这种方法(因为它允许控制器非常短......)。还有更多:我认为(但要尝试) findOrFail('wrong uuid') 将返回相同的 postgre 错误。
  • IRC 模型绑定确实使用了findOrFail(),当启用调试时,它会在不成功的结果上抛出ModelNotFoundException。禁用的 Laravel 将捕获该异常并为其呈现可自定义的页面。我认为问题无关。请参阅我对这个问题的评论。
猜你喜欢
  • 2023-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-31
  • 2013-11-15
  • 2019-12-03
  • 2015-05-04
相关资源
最近更新 更多