Laravel 验证请求和 API 路由 POST 参数

问题描述 投票:0回答:3

我在表单请求验证方面遇到了一个小问题,以及如何使用一个 API 路由来处理它。

我需要创建的资源依赖于其他资源。

(此处

EmailSettings
属于租户)

所以我的路线的外观应该类似于:/api/tenants/{id}/email_settings

我的请求验证需要几个字段,包括

tenantId
:

public function rules() {
    return [
        'email' => 'bail|required|email|unique:email_settings,email',
        'name' => 'bail|required',
        'username' => 'bail|required',
        'password' => 'bail|required'
        'imapHost' => 'bail|required',
        'imapPort' => 'bail|required',
        'imapEncryption' => 'bail|required',
        'imapValidateCert' => 'bail|required',
        'smtpHost' => 'bail|required',
        'smtpPort' => 'bail|required',
        'smtpEncryption' => 'bail|required',
        'tenantId' => 'bail|required',
    ];
}

我像这样发送请求:

try {
    const response = await this.tenantForm.post('/api/tenants')
    let newTenant = helpers.getNewResourceFromResponseHeaderLocation(response)
    let tenantId = parseInt(newTenant.id);
    try {
        await this.emailSettingsForm.post('/api/tenants/' + tenantId + '/email_settings')
        this.requestAllTenants()
    } catch ({response}) {
        $('.second.modal').modal({blurring: true, closable: false}).modal('show');
    }
} catch ({response}) {
    $('.first.modal').modal({blurring: true}).modal('show');
}

因此

tenantId
作为参数传递,而不是在请求正文中传递,以遵守 REST 约定。 但问题出在我的控制器中,当我合并数据以创建资源时,验证已经仅在合并之前的正文数据上进行了。

public function store(EmailSettingValidation $request, $tenant_id) {
    $emailSetting = $this->emailSettingService->create(
        array_merge($request->all(), compact($tenant_id))
    );
    return $this->response->created($emailSetting);
}

那么正确处理的最佳方法是什么?

  • 在body中传递id?看起来很乱
  • 使用Validator手动验证?我更愿意保留表单验证
  • 删除
    tenantId
    规则并手动检查?

有什么建议吗?

php laravel rest validation
3个回答
1
投票

如果你像这样定义你的 api 路由:

Roue::post('tenants/{tenant}/emails_settings', 'Controller@store');

并修改您的控制器方法,以使用与您的路由定义相匹配的变量名称来键入提示模型:

public function store(EmailSettingValidation $request, Tenant $tenant) {}

然后 Laravel 会自动通过 ID 找到租户并将其注入到控制器中,如果不存在则抛出 ModelNotFoundException (404)。这应该负责验证 id。

授权是另一回事。


0
投票

所以我发现触发404的解决方案如下:

  • 从电子邮件设置验证中删除tenantId
  • 添加一个提供程序以在发生异常“ModelNotFoundException”时添加自定义错误,如下所示 Laravel 中的模型与 Dingo 没有查询结果 - 如何在失败时做出 RESTful 响应?
  • 如果 ID 无效,请尝试使用 findOrFail 方法抛出此异常:

    public function store(EmailSettingValidation $request, $tenant_id) {
        Tenant::findOrFail($tenant_id);
        $emailSetting = $this->emailSettingService->create(
            array_merge($request->all(), ['tenantId' => $tenant_id])
        );
        return $this->response->created($emailSetting);
    }
    

0
投票

Travis Britz 和 Guillaumehanotel 各有一半的答案,但你仍然遗漏了一个细节。

来自 Travis Britz - 是的,在 URI 中包含tenant_id,以便将其注入到控制器中。 来自 Guillaumehanotel - 还在控制器的 Id 中使用了 Eloquent findOrFail(或者控制器用来执行此操作的任何类,例如存储库或服务类)。

您缺少的最后一部分是处理错误。如果您愿意,可以在控制器中执行此操作,但我通常喜欢为整个系统制定一条规则,即来自

Illuminate\Database\Eloquent\ModelNotFoundException
的异常应始终导致 404。

转到

findOrFail()

。我很确定 Laravel 会自动为你生成这个文件的主要版本,但如果你还没有,它应该看起来像这样:


app/Exceptions/Handler.php

Laravel 基本上有一个系统范围的 
<?php namespace App\Exceptions; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; /** * Class Handler * @package App\Exceptions */ class Handler extends ExceptionHandler { /** * Render an exception into an HTTP response. * * For our API, we need to override the call * to the parent. * * @param \Illuminate\Http\Request $request * @param \Exception $e * @return \Illuminate\Http\Response */ public function render($request, Exception $error) { $exception = [ 'title' => 'Internal Error', 'message' => $error->getMessage(); ]; $statusCode = 500; $headers = [ 'Content-Type', 'application/json' ]; return response()->json($exception, $statusCode, $headers, JSON_PRETTY_PRINT); } }

,它首先通过这里发送所有错误。这就是当您处于调试模式时错误如何呈现为浏览器可以实际解释的内容,而不是直接终止进程。这也让您有机会应用一些特殊规则。


因此,您需要做的就是告诉

try/catch

更改当它看到只能来自

Handler::render()
的错误类型时发生的默认错误代码。 (这种事情就是为什么创建自己的“命名异常”总是好的,即使它们除了继承基类
findOrFail()
之外什么也不做。)

只需在

\Exception

返回任何内容之前添加此内容:


render()

© www.soinside.com 2019 - 2024. All rights reserved.