我在表单请求验证以及如何使用一个API路由处理它方面遇到了一个小问题。
我需要创建的资源取决于其他资源。
(此处的电子邮件设置属于租户)
所以我的路线外观应该类似于:/ 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);
}
那么正确处理它的最佳方法是什么?
有什么建议吗? 谢谢
答案 0 :(得分:1)
如果您这样定义api路由:
Roue::post('tenants/{tenant}/emails_settings', 'Controller@store');
并修改您的控制器方法以使用匹配的变量名称键入模型提示:
public function store(EmailSettingValidation $request, Tenant $tenant) {}
Laravel将通过ID自动找到租户,并将其注入到控制器中,如果不存在,则会抛出ModelNotFoundException(404)。那应该注意验证ID。
授权是另一回事。
答案 1 :(得分:0)
因此,我发现触发404的解决方案如下:
如果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);
}
答案 2 :(得分:0)
特拉维斯·布里茨(Travis Britz)和纪尧姆·海诺特(Guillaumehanotel)的答案只有一半,但您仍然缺少细节。
来自Travis Britz-是的,在URI上包含tenant_id,以便将其注入到控制器中。 来自Guillaumehanotel-还用了雄辩的findOrFail作为Controller中该ID的标识符(或Controller用来实现此目的的任何类,例如Repository或Service类)。
尽管您最后遗失的是处理错误。您可以根据需要在Controller中执行此操作,但是我通常希望将整个Illuminate\Database\Eloquent\ModelNotFoundException
中出现的findOrFail()
异常作为规则,以产生404。
转到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);
}
}
Laravel基本上具有系统范围的try/catch
,它首先通过此处发送所有错误。这就是将错误呈现到浏览器可以在您处于调试模式时真正解释的内容的方式,而不是直接杀死该过程。这也使您有机会应用一些特殊规则。
因此,您所需要做的就是告诉Handler::render()
更改默认错误代码,该错误代码在看到只能来自findOrFail()
的错误类型时发生。 (这种情况就是为什么创建自己的“命名异常”总是很好的,即使它们除了继承基类\Exception
之外什么也不做,也是如此。)
只需在render()
返回任何内容之前添加它:
if ($error instanceof Illuminate\Database\Eloquent\ModelNotFoundException) {
$statusCode = 404;
}