如何在使用我自己的Laravel API时遵循Don't Repeat Yourself原则


How Follow the Don't Repeat Yourself Principle When Consuming My Own Laravel API?

我正在开发一个Laravel 4应用程序,它将通过JSON REST API和Web UI对我的数据集进行相同的CRUD操作。似乎为了防止违反DRY原则,我的UI应该通过将来自UI的所有请求路由回API来使用我自己的API。但我不确定实现这一目标的最佳方法是什么。假设我会有单独的UI和API控制器,并以某种方式路由请求。或者我应该寻找一种完全不同的方法?

我实际上是在用同样的想法进行修修补补,它非常整洁。使用Laravel,您确实有能力发出内部请求(有些人可能将其称为HMVC,但我不会)。以下是内部请求的基本内容:

$request = Request::create('/api/users/1', 'GET');
$response = Route::dispatch($request);

$response现在将包含API返回的响应。通常,这将返回一个JSON编码的字符串,这对客户端来说很好,但对于内部API请求来说就不那么好了。这里需要扩展一些内容,但基本思路是通过内部调用返回实际对象,对于外部请求返回格式化的JSON响应。你可以使用像$response->getOriginalContent()这样的东西来做这种事情。

你应该做的是构建某种内部Dispatcher,允许你调度API请求并返回原始对象。调度程序还应该处理格式错误的请求或错误的响应,并抛出异常来匹配。

这个想法本身是可靠的。但是规划API是一项艰巨的工作。我建议你列出所有预期的端点,起草几个API版本,然后选择最好的一个。

注意:正如vcardillo在下面指出的,路由过滤器不是用这些方法调用的。

我现在也在做同样的事情,Jason的回答让我朝着一个很好的方向前进。看着Symfony'Component'HttpFoundation'Request文档,我弄清楚了如何POST,以及我需要做的其他一切。假设您正在使用表单,这里有一些代码可以帮助您:

得到:

$request = Request::create('/api/users/1', 'GET');
$response = Route::dispatch($request);

:

$request = Request::create('/api/users/1', 'POST', Input::get());
$response = Route::dispatch($request);

POST w/cookies

$request = Request::create('/api/users/1', 'POST', Input::get(), Cookie::get('name'));
$response = Route::dispatch($request);

POST w/files

$request = Request::create('/api/users/1', 'POST', Input::get(), null, Input::file('file'));
$response = Route::dispatch($request);

我希望这能帮助到别人。如果你没有使用表单,或者你没有使用Laravel的Input/Cookie facade,用你自己的内容替换Input/Cookie facade。

Taylor Otwell建议使用app()->handle()而不是Route::dispatch()来实现清晰的请求。

对于Route::dispatch($request),我注意到如果您的非get请求的端点(HTTP请求体上的参数)使用依赖注入'Illuminate'Http'Request'Illuminate'Foundation'Http'FormRequest扩展实例,参数的状态,cookie,文件等都来自原始 HTTP请求。例如,用于应用程序的控制器动作方法。

如果你的应用控制器和API控制器的参数名和post方法类型是相同的,你不会注意到差异,因为原始参数值被传递了。但是当你手动组装Request::create()的第三个参数时,Route::dispatch()会导致它被忽略。

app()->handle()修复了Laravel请求生命周期中的上下文问题。

警告: app()->handle()影响Illuminate'Support'Facades'Request,用这个新的请求实例刷新它。作为连锁反应,在app()->handle()之后调用Request::isXmlHttpRequest()redirect()->back()将导致不可预测的行为。我建议跟踪你的原始请求的上下文,而不是使用redirect()->to(route('...')),这样你就可以严格控制你的应用程序的流程和状态。

考虑到所有这些极端情况,最好使用Guzzle HTTP客户端执行手动curl。

如果您想在内部使用护照登录api,那么您需要将参数添加到原始请求:

    protected function manualLogin(Request $request)
    {
        $email = $request->input('email');
        $password = $request->input('password');
        $request->request->add([
        'username' => $email,
        'password' => $password,
        'grant_type' => 'password',
        'client_id' => $clientID,
        'client_secret' => $clientSecret,
        'scope' => '*']);
    $newRequest = Request::create('/oauth/token', 'post');
    return Route::dispatch($newRequest)->getContent();
}

如果您正在使用自己的API,请使用app()->handle()而不是Derek MacDonald建议的Route::dispatch()

app()->handle()创建一个新的请求,而Route::dispatch()在堆栈中运行路由,有效地忽略了你正在发送的请求的一部分参数。

Edit:只是一个提醒。Taylor Otwell建议不要使用子请求进行内部API调用,因为它们会弄乱当前的路由。您可以使用HTTP API客户端(如Guzzle)来调用API。

你可以使用Optimus的API消费者,API是干净和简单的,例如做一个内部请求:

$response = app()->make('apiconsumer')->post('/oauth/token', $data);

在它的核心,它使用Illuminate'Routing'RouterIlluminate'Http'Request来调用

// create the request
$this->request->create($uri, $method, $data, [], [], $server, $content);
// get the response
$response = $this->router->prepareResponse($request, $this->app->handle($request));