Nova Kwok's Awesome Blog

在 Laravel 中向外部 API 发起请求

为了程序的分离起见,并且实现一个并不存在的微服务架构(其实是有一个本地的 Django 项目已经写好了逻辑,不想用 PHP 重写了),在已经有的一个 Laravel 项目上需要请求一个外部的 API,我们可以使用一个库:Guzzle,为了演示方便,我将使用 Django 作为后端,输出一个简单的 JSON 格式数据返回,前端用 Laravel (当然,日后肯定是要切换到一些专门的前端框架上面的)接住,大致示意图如下(我画图技术是越来越差了…):

Backend(Django)

由于是初学,我们把功能定义的简单一些,在 urls.py 中:

from django.urls import path

from . import views

urlpatterns = [
path('sigma',views.sigma,name='sigma')
]

在对应的 views.py 中直接定义 sigma 函数:

import json
from django.http import HttpResponse,JsonResponse
# ... 省略部分代码
def sigma(request):
    d = dict()
    d['title'] = 'Sigma Title'
    d['time'] = '2019-01-29'
    return JsonResponse(d)

Frontend(Laravel)

在 Laravel 中安装 Guzzle

composer require guzzlehttp/guzzle

然后在对应的控制器中使用:

use GuzzleHttp\Client;
//...
public function sigma()
{
    $client = new Client();
    $response = $client->get('http://127.0.0.1:4000/score/sigma');
    return json_decode((string) $response->getBody(), true);
}

注意,最后一行 json_decode((string) $response->getBody(), true); 的写法,在本文中我们使用的 Guzzle 6,在 PSR-7 中已经无法直接使用类似 $response->json() 的用法了,原因如下:

Guzzle has switched over to PSR-7 only interfaces. In order to keep some of the old methods on responses like json, xml, effectiveUri, Guzzle would need to rely on a concrete implementation of a PSR-7 response. This would then require Guzzle users to rely on a concretion rather than an abstraction (an interface). This would then lead to needing to incessantly wrap normal PSR-7 responses with Guzzle specific responses or you would need to check the class of each response object you receive from Guzzle to ensure that it has the json method. Because Guzzle has a middleware system that is meant to essentially decorate clients, it makes much more sense to just get rid of the old Guzzle specific methods and use only PSR-7 methods.

Furthermore, the json method was a polarizing feature: some people wanted it to return associative arrays and others wanted it to return stdclass objects. Arguably, it shouldn’t have been on the response object anyways as it’s too specific IMO (i.e., we did not have yaml(), ini(), and other specific format parsers built-in). It’s now just a matter of calling json_decode($response->getBody()).

本文我们给出的是发起的 GET 请求,除此之外,我们还可以使用一些其他的指令,例如:

$response = $client->get('http://httpbin.org/get');
$response = $client->delete('http://httpbin.org/delete');
$response = $client->head('http://httpbin.org/get');
$response = $client->options('http://httpbin.org/get');
$response = $client->patch('http://httpbin.org/patch');
$response = $client->post('http://httpbin.org/post');
$response = $client->put('http://httpbin.org/put');

且,在初始化 Client 的时候可以定义 baseurl,例如:

$client = new Client([
    // Base URI is used with relative requests
    'base_uri' => 'http://httpbin.org',
    // You can set any number of default request options.
    'timeout'  => 2.0,
]);

对于 baseurl 的使用我们有如下文档表格可以参考:

base_uriURIResult
http://foo.com/barhttp://foo.com/bar
http://foo.com/foo/barhttp://foo.com/bar
http://foo.com/foobarhttp://foo.com/bar
http://foo.com/foo/barhttp://foo.com/foo/bar
http://foo.comhttp://baz.comhttp://baz.com
http://foo.com/?barbarhttp://foo.com/bar

参考资料

1.no more json() method for responses? #1106

2.Guzzle 6: no more json() method for responses

3.Guzzle Documentation

#Chinese #Laravel