本文主要是介绍Laravel Lumen RESTFul API 扩展包:Dingo API(一) —— 安装配置篇,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
https://xueyuanjun.com/post/3822
Dingo API 为开发者提供了一整套工具以便帮助你轻松、快捷的构建自己的API。这些工具包括:
- 内容协商
- 多认证适配器
- API版本
- 频率限制
- 响应转化和格式化
- 错误及异常处理
- 内部请求
- API文档
1、安装
安装该扩展包之前需要保证已经安装以下程序:
- Laravel 5.1+ 或 Lumen 5.1+
- PHP 5.5.9+
然后通过如下Composer命令安装扩展包:
1
composer require dingo/api:1.0.x@dev
安装完成后的操作取决于你使用的是哪个框架。
Laravel
在config/app.php
中注册服务提供者到providers
数组:
1
Dingo\Api\Provider\LaravelServiceProvider::class
如果你想要自定义扩展包配置可以将其发布到config
目录下:
1
php artisan vendor:publish --provider="Dingo\Api\Provider\LaravelServiceProvider"
Lumen
打开bootstrap/app.php
注册需要的服务提供者:
1
$app->register(Dingo\Api\Provider\LumenServiceProvider::class);
门面
有两个门面来处理这个扩展包,你可以添加任意一个:
Dingo\Api\Facade\API
Dingo\Api\Facade\Route
2、配置
基本上所有功能都已经预先配置好,所以不需要什么改动就可以开始构建API,你可以使用.env
文件实现大部分配置,对于一些细小的地方需要发布配置文件(Laravel)进行微调或者在bootstrap/app.php
中配置(Lumen),你还可以使用AppServiceProvider
的boot
方法。
标准树
有三个不同的树:x
、prs
和vnd
,你使用的标准树取决于你所开发的项目:
- 未注册的树(
x
)主要用于本地或私有环境 - 个人树(
prs
)主要用于非商业销售的项目 - 供应商树(
vnd
)主要用于公开的以及商业销售的项目
你可以在.env
中配置:
1
API_STANDARDS_TREE=vnd
子类型(Subtype)
子类型通常是应用或项目的简称,或小写格式。你也可以在.env
中配置:
1
API_SUBTYPE=myapp
前缀和子域名
如果你曾经开发过API应该知道大部分API都是以子域名(或某个前缀)形式提供服务,前缀或子域名是必须的,并且同时只有一个,不要讲版本号作为前缀或子域名,因为版本号通常通过Accept
头进行处理。
你可以在.env
文件中配置前缀:
1
API_PREFIX=api
或子域名:
1
API_DOMAIN=api.myapp.com
版本
这里的版本指的是API的默认版本,可以在没有提供版本的时候使用这个版本,或者作为生成API文档时的默认版本。
你可以在.env
文件中配置它:
1
API_VERSION=v1
名字
API的名字只有在使用API Blueprint命令生成文档的时候才用到,这个名字作为默认名字以免生成文档时需要手动指定名字。
你可以在.env
文件中配置它:
1
API_NAME=My API
带条件的请求
由于缓存API请求的时候会使用客户端缓存功能,所以默认开启了带条件的请求,你可以在配置文件.env
中配置该选项:
1
API_CONDITIONAL_REQUEST=false
Strict模式
Strict模式要求客户端发送Accept
头而不是默认在配置文件中指定的版本,这意味着你不能通过Web浏览器浏览API。
如果Strict模式开启并且使用了无效的Accept
头,API会抛出一个Symfony\Component\HttpKernel\Exception\BadRequestHttpException
异常。
你可以在.env
中配置这个选项:
1
API_STRICT=false
认证提供者
默认只开启了basic
认证,如果要对认证进行更复杂的配置,需要在服务提供者(Laravel)或启动文件(Lumen)设置:
1
$app['Dingo\Api\Auth\Auth']->extend('oauth', function ($app) {
2
return new Dingo\Api\Auth\Provider\JWT($app['Tymon\JWTAuth\JWTAuth']);
3
});
Throttling/频率限制
默认频率限制是被禁用的,你可以注册自定义的带有频率限制的throttle或者使用已经存在的认证及取消认证throttle。
要进行更加复杂的配置同样需要在服务提供者或启动文件中操作:
1
$app['Dingo\Api\Http\RateLimit\Handler']->extend(function ($app) {
2
return new Dingo\Api\Http\RateLimit\Throttle\Authenticated;
3
});
响应转化器
Fractal
是默认的响应转化器。你可以在.env
中配置,但如果要实现更加复杂的配置还是需要在服务提供者或启动文件操作:
1
$app['Dingo\Api\Transformer\Factory']->setAdapter(function ($app) {
2
$fractal = new League\Fractal\Manager;
3
4
$fractal->setSerializer(new League\Fractal\Serializer\JsonApiSerializer);
5
6
return new Dingo\Api\Transformer\Adapter\Fractal($fractal);
7
});
响应格式
默认的响应格式是JSON,你可以在配置文件.env
中配置默认的响应格式:
1
API_DEFAULT_FORMAT=json
更加高级的格式配置需要发布配置文件到config
目录或者在服务提供者及启动文件中操作:
1
Dingo\Api\Http\Response::addFormatter('json', new Dingo\Api\Http\Response\Format\Jsonp);
错误格式
如果扩展包遇到错误将会尝试生成错误响应而不是将异常抛给用户,错误格式可以进行自定义配置。该配置必须在config
目录下对应的配置文件(Laravel)或启动文件(Lumen)中实现:
1
$app['Dingo\Api\Exception\Handler']->setErrorFormat([
2
'error' => [
3
'message' => ':message',
4
'errors' => ':errors',
5
'code' => ':code',
6
'status_code' => ':status_code',
7
'debug' => ':debug'
8
]
9
]);
调试模式
如果开启了调试模式的话吗,生成的错误信息会被扩展包放到debug
键中,并与堆栈跟踪信息一起显示出来。
你可以在配置文件.env
中配置:
1
API_DEBUG=true
Laravel
Laravel & Lumen RESTFul API 扩展包:Dingo API(二) —— 创建 API Endpoint(路由)
由 学院君 创建于4年前 版本号 #2 22815 views 17 likes 0 collects
Endpoint 就是路由的另一种术语,当我们讨论API时,很多人习惯将访问的路由看作Endpoint。
1、版本号
为了避免和主应用的路由混在一起,Dingo API使用了自己的路由器,正因如此我们首先需要获取API路由器实例来创建Endpoint:
1
$api = app('Dingo\Api\Routing\Router');
接下来需要定义版本号,从而可以为多版本API创建同样的Endpoint以便后续回滚:
1
$api->version('v1', function ($api) {
2
3
});
如果你想要某个组响应多个版本的API可以传递多版本数组:
1
$api->version(['v1', 'v2'], function ($api) {
2
3
});
这里的版本号可以看作和框架的标准路由分组一样传递数组属性作为第二个参数:
1
$api->version('v1', ['middleware' => 'foo'], function ($api) {
2
3
});
还可以嵌套普通版分组以便后续实现更复杂的自定义Endpoint:
1
$api->version('v1', function ($api) {
2
$api->group(['middleware' => 'foo'], function ($api) {
3
// Endpoints registered here will have the "foo" middleware applied.
4
});
5
});
2、创建Endpoint
有了版本号之后就可以开始使用$api
创建Endpoint了:
1
$api->version('v1', function ($api) {
2
$api->get('users/{id}', 'App\Api\Controllers\UserController@show');
3
});
因为Endpoint以版本号进行分组,所以你可以使用同样的URI为同一Endpoint创建不同的响应:
1
$api->version('v1', function ($api) {
2
$api->get('users/{id}', 'App\Api\V1\Controllers\UserController@show');
3
});
4
5
$api->version('v2', function ($api) {
6
$api->get('users/{id}', 'App\Api\V2\Controllers\UserController@show');
7
});
还可以使用各自的方法注册资源和控制器。
注意:与Laravel不同,这里必须指定控制器的完整命名空间。
命名路由并生成URL
命名路由可以帮助我们轻松生成对应URL。你可以像在Laravel中一样命名路由:
1
$api->get('users/{id}', ['as' => 'users.index', 'uses' => 'Api\V1\UserController@show']);
然后你可以生成URL到这个命名路由:
1
app('Dingo\Api\Routing\UrlGenerator')->version('v1')->route('users.index');
必须提供一个版本号以便URL可以基于该版本号生成,同时,你可以在不同版本号中使用同一个命名路由。
3、在控制台查看路由
如果你使用的是Laravel 5.1,可以使用Artisan命令查看注册路由:
1
$ php artisan api:routes
该命令和Laravel中的route:list
命令一样。
Laravel & Lumen RESTFul API 扩展包:Dingo API(三) —— Response(响应)
由 学院君 创建于4年前 版本号 #2 28769 views 11 likes 0 collects
一个API的功能主要是获取请求并返回响应给客户端,响应的格式是多样的,比如JSON,返回响应的方式也是多样的,这取决于当前构建的API的复杂度以及对未来的考量。
返回响应最简单的方式是直接从控制器返回数组或对象,但不是每个响应对象都能保证格式正确,所以你要确保它们实现了ArrayObject
或者Illuminate\Support\Contracts\ArrayableInterface
接口:
1
class UserController
2
{
3
public function index()
4
{
5
return User::all();
6
}
7
}
在本例中,User
类继承自Illuminate\Database\Eloquent\Model
,这意味着返回的是可以被格式化为数组的数据,当然也可以返回单个用户:
1
class UserController
2
{
3
public function show($id)
4
{
5
return User::findOrFail($id);
6
}
7
}
Dingo API会自动将响应格式化为JSON格式并设置Content-Type
头为application/json
。
1、响应构建器
响应构建器提供了平滑的接口以便我们轻松构建更多自定义的响应。响应构建器通常与转换器(Transformer)一起使用。
要使用响应构建器控制器需要使用Dingo\Api\Routing\Helpers
trait,为了让每个控制器都可以使用这个trait,我们将其放置在API基类控制器Controller
中:
1
use Dingo\Api\Routing\Helpers;
2
use Illuminate\Routing\Controller;
3
4
class BaseController extends Controller
5
{
6
use Helpers;
7
}
现在可以定义一个继承自该控制器的控制器,在这些控制器中可以通过$response
属性来访问响应构建器。
数组响应
1
class UserController extends BaseController
2
{
3
public function show($id)
4
{
5
$user = User::findOrFail($id);
6
return $this->response->array($user->toArray());
7
}
8
}
单个Item响应
1
class UserController extends BaseController
2
{
3
public function show($id)
4
{
5
$user = User::findOrFail($id);
6
return $this->response->item($user, new UserTransformer);
7
}
8
}
集合响应
1
class UserController extends BaseController
2
{
3
public function index()
4
{
5
$users = User::all();
6
return $this->response->collection($users, new UserTransformer);
7
}
8
}
分页响应
1
class UserController extends BaseController
2
{
3
public function index()
4
{
5
$users = User::paginate(25);
6
return $this->response->paginator($users, new UserTransformer);
7
}
8
}
无内容响应
1
return $this->response->noContent();
创建响应
1
return $this->response->created();
还可以将位置信息作为创建资源的第一个参数:
1
return $this->response->created($location);
错误响应
你可以使用多种内置错误生成错误响应:
1
// A generic error with custom message and status code.
2
return $this->response->error('This is an error.', 404);
3
4
// A not found error with an optional message as the first parameter.
5
return $this->response->errorNotFound();
6
7
// A bad request error with an optional message as the first parameter.
8
return $this->response->errorBadRequest();
9
10
// A forbidden error with an optional message as the first parameter.
11
return $this->response->errorForbidden();
12
13
// An internal error with an optional message as the first parameter.
14
return $this->response->errorInternal();
15
16
// An unauthorized error with an optional message as the first parameter.
17
return $this->response->errorUnauthorized();
添加额外的响应头
使用了上述方法之后还可以通过添加响应头来自定义响应:
1
return $this->response->item($user, new UserTransformer)->withHeader('X-Foo', 'Bar');
添加元数据
某些转化层可能会使用元数据(meta data),这在你需要提供额外与资源关联的数据时很有用:
1
return $this->response->item($user, new UserTransformer)->addMeta('foo', 'bar');
还可以设置元数据数组替代多个方法链的调用:
1
return $this->response->item($user, new UserTransformer)->setMeta($meta);
设置响应状态码
1
return $this->response->item($user, new UserTransformer)->setStatusCode(200);
2、自定义响应格式
在安装配置中我们已经简单接触过响应格式,默认情况下Dingo API会自动使用JSON格式并设置相应的Content-Type
头。除了JSON之外还有一个JSONP格式,改格式会将响应封装到一个回调中。要注册改格式只需要简单将配置文件(Laravel)或启动文件(Lumen)中的默认JSON格式替换成JSONP即可:
1
'formats' => [
2
'json' => 'Dingo\Api\Http\Response\Format\Jsonp'
3
]
或者:
1
Dingo\Api\Http\Response::addFormatter('json', new Dingo\Api\Http\Response\Format\Jsonp);
默认情况下回调参数默认查询字符串是callback,这可以通过修改构造函数的第一个参数来设置。如果查询字符串不包含任何参数将会返回JSON响应。
你还可以注册并使用自己需要的响应格式,自定义的格式对象需要继承自Dingo\Api\Http\Response\Format\Format
类,同时还要实现如下这些方法:formatEloquentModel
, formatEloquentCollection
, formatArray
以及 getContentType
。
3、Morphing 和 Morphed 事件
在Dingo API发送响应之前会先对该响应进行转化(morph),这个过程包括运行所有转换器(Transformer)以及通过配置的响应格式发送响应。如果你需要控制响应如何被转化可以使用ResponseWasMorphed
和ResponseIsMorphing
事件。
我们在app/Listeners
中为事件创建监听器:
1
use Dingo\Api\Event\ResponseWasMorphed;
2
3
class AddPaginationLinksToResponse
4
{
5
public function handle(ResponseWasMorphed $event)
6
{
7
if (isset($event->content['meta']['pagination'])) {
8
$links = $event->content['meta']['pagination']['links'];
9
10
$event->response->headers->set(
11
'link',
12
sprintf('; rel="next", ; rel="prev"', $links['links']['next'], $links['links']['previous'])
13
);
14
}
15
}
16
}
然后通过在EventServiceProvider
中注册事件及其对应监听器来监听该事件:
1
protected $listen = [
2
'Dingo\Api\Event\ResponseWasMorphed' => [
3
'App\Listeners\AddPaginationLinksToResponse'
4
]
5
];
现在所有包含分页链接的响应也会将这些链接添加到Link
头。
Laravel & Lumen RESTFul API 扩展包:Dingo API(四) —— 错误和异常响应
由 学院君 创建于4年前 版本号 #2 19145 views 13 likes 0 collects
在构建API的时候处理错误是一件痛苦的事儿,在Dingo API中,你不需要手动构建错误响应,只需要抛出一个继承自Symfony\Component\HttpKernel\Exception\HttpException
的异常,API会自动为你处理这个响应。
下面是Dingo API内置的Symfony异常:
异常 | 状态码 |
Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException | 403 |
Symfony\Component\HttpKernel\Exception\BadRequestHttpException | 400 |
Symfony\Component\HttpKernel\Exception\ConflictHttpException | 409 |
Symfony\Component\HttpKernel\Exception\GoneHttpException | 410 |
Symfony\Component\HttpKernel\Exception\HttpException | 500 |
Symfony\Component\HttpKernel\Exception\LengthRequiredHttpException | 411 |
Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException | 405 |
Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException | 406 |
Symfony\Component\HttpKernel\Exception\NotFoundHttpException | 404 |
Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException | 412 |
Symfony\Component\HttpKernel\Exception\PreconditionRequiredHttpException | 428 |
Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException | 503 |
Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException | 429 |
Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException | 401 |
Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException | 415 |
下面是一个示例,当我们尝试更新一条已经被别人更新过的记录时抛出一个ConflictHttpException
异常:
1
$api->version('v1', function ($api) {
2
$api->put('user/{id}', function ($id) {
3
$user = User::find($id);
4
5
if ($user->updated_at > app('request')->get('last_updated')) {
6
throw new Symfony\Component\HttpKernel\Exception\ConflictHttpException('User was updated prior to your request.');
7
}
8
9
// No error, we can continue to update the user as per usual.
10
});
11
});
Dingo API会自动捕获抛出的异常并将其转化为JSON格式,响应的HTTP状态码也会相应更改以匹配这个异常,ConflictHttpException
对应的HTTP状态码是409
,默认的JSON格式错误信息如下:
1
{
2
"message": "User was updated prior to your request.",
3
"status_code": 409
4
}
1、资源异常
以下是资源异常,每个异常都会返回422
状态码:
1
Dingo\Api\Exception\DeleteResourceFailedException
2
Dingo\Api\Exception\ResourceException
3
Dingo\Api\Exception\StoreResourceFailedException
4
Dingo\Api\Exception\UpdateResourceFailedException
这些异常特殊之处在于你可以将创建、更新或者删除资源时遇到的验证错误传递到这些异常中。
下面我们就来看一个创建新用户验证失败抛出StoreResourceFailedException
异常的例子:
1
$api->version('v1', function ($api) {
2
$api->post('users', function () {
3
$rules = [
4
'username' => ['required', 'alpha'],
5
'password' => ['required', 'min:7']
6
];
7
8
$payload = app('request')->only('username', 'password');
9
10
$validator = app('validator')->make($payload, $rules);
11
12
if ($validator->fails()) {
13
throw new Dingo\Api\Exception\StoreResourceFailedException('Could not create new user.', $validator->errors());
14
}
15
16
// Create user as per usual.
17
});
18
});
Dingo API会自动捕获抛出的异常并将其转化为JSON格式,响应的HTTP状态码也会更改为与异常相匹配的值,资源异常会返回422
状态码以及如下JSON格式错误信息:
1
{
2
"message": "Could not create new user.",
3
"status_code": 422,
4
"errors": {
5
"username": [
6
"The username field is required."
7
],
8
"password": [
9
"The password field is required."
10
]
11
}
12
}
2、自定义HTTP异常
你可以创建自定义的HTTP异常,前提是它们继承自Symfony\Component\HttpKernel\Exception\HttpException
或者实现了Symfony\Component\HttpKernel\Exception\HttpExceptionInterface
接口。
3、自定义异常响应
如果你需要自定义异常返回的响应可以注册一个异常处理器:
1
app('Dingo\Api\Exception\Handler')->register(function (Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException $exception) {
2
return Response::make(['error' => 'Hey, what do you think you are doing!?'], 401);
3
});
现在如果认证失败我们会显示如下JSON格式信息:
1
{
2
"error": "Hey, what do you think you are doing!?"
3
}
4、表单请求
如果你使用表单请求,那么需要继承API表单请求基类或者实现自己的类。API请求基类会检查输入请求是否是请求API,如果是的话当验证失败会抛出Dingo\Api\Exception\ValidationHttpException
异常。这个异常会被Dingo API渲染并返回错误响应。
如果你想要实现自己的表单请求,则必须重载failedValidation
和failedAuthorization
方法,这些方法必须抛出上述其中一种异常而不是Laravel抛出的HTTP异常。
Laravel & Lumen RESTFul API 扩展包:Dingo API(五) —— 转化器(Transformer)
由 学院君 创建于4年前 版本号 #2 25574 views 11 likes 0 collects
1、简介
通过转化器,你可以将对象转化为数组,并强制转化整型和布尔类型,包括分页结果和嵌套关联。
本章节我们主要讨论转化器及其使用,这里的转化器包括以下两层意思:
- 转化层(transformation layer)是一个准备和处理转化器的库
- 转化器(transformer)是一个获取原始数据并将其转化为数组格式的类,处理器的处理方式取决于转化层。
2、使用转化器
有多种方式使用转化器。
为某个类注册转化器
当你为某个类注册转化器后就可以从路由返回这个类(假设它可以被转化为数组)并自动经过转化器处理:
1
app('Dingo\Api\Transformer\Factory')->register('User', 'UserTransformer');
这在使用模型的简单API中很有用,因为你可以从路由中直接返回模型。
使用响应构建器
参考响应构建器章节。
2、Fractal
Fractal是Dingo API默认的转化层,它提供了一系列有用的特性来保持数据的一致性。
使用Fractal之前建议好好阅读其官方文档。
全自动关联关系渴求式加载
当使用Fractal的内置功能嵌入关联关系时确保其命名和模型中的命名一致。扩展包会自动为你渴求式加载这些关联关系。
高级配置
Fractal以默认配置注册为默认转化层,要手动配置需要在服务提供者中实例化Dingo\Api\Transformer\Adapter\Fractal
实例:
1
$this->app['Dingo\Api\Transformer\Factory']->setAdapter(function ($app) {
2
return new Dingo\Api\Transformer\Adapter\Fractal(new League\Fractal\Manager, 'include', ',');
3
});
如果你使用的是Lumen可以在启动文件(bootstrap.php)中这么做:
1
app('Dingo\Api\Transformer\Factory')->setAdapter(function ($app) {
2
return new Dingo\Api\Transformer\Adapter\Fractal(new League\Fractal\Manager, 'include', ',');
3
});
通过响应构建器实现高级使用
与响应构建器相结合使用Fractal通常是从控制器返回数据的最佳方式。响应构建器上的item
、collection
和paginator
方法都可以接收额外参数用于自定义Fractal。
资源key
1
return $this->item($user, new UserTransformer, ['key' => 'user']);
使用回调
Fractal转化层允许你注册一个创建资源之后触发的回调,这个回调接收League\Fractal\Resource\Item
或者League\Fractal\Resource\Collection
作为第一个参数,League\Fractal\Manager
实例作为第二个参数。然后你可以使用这个回调在更复杂的层级上与资源进行交互。
最常见的使用案例就是设置分页游标或者修改响应串行:
1
return $this->collection($users, new UserTransformer, [], function ($resource, $fractal) {
2
$resource->setCursor($cursor);
3
});
如果你不想传递资源key参数可以省略这个空数组:
1
return $this->collection($users, new UserTransformer, function ($resource, $fractal) {
2
$fractal->setSerializer(new CustomSerializer);
3
});
3、自定义转化层
如果你想对数据转化进行更多的自定义,可以在Dingo API中实现自己的转化层。这需要创建一个实现Dingo\Api\Contract\Transformer\Adapter
的类并且实现transform
方法:
1
use Dingo\Api\Http\Request;
2
use Dingo\Api\Transformer\Binding;
3
use Dingo\Api\Contract\Transformer\Adapter;
4
5
class MyCustomTransformer implements Adapter
6
{
7
public function transform($response, $transformer, Binding $binding, Request $request)
8
{
9
// Make a call to your transformation layer to transformer the given response.
10
}
11
}
transform
是唯一必须的方法,你可以自由添加自己想要的其他方法。transform
方法的目的是获取$response
,然后和$transformer
一起传递给转化层处理,然后转化层会返回一个数组,最终这个数组被transform
方法返回。如果你的转化层很简单,可以完全在这个类中实现所有逻辑。
$bindings
参数在你的转化层包含更多特性比如添加元数据或者允许开发者通过回调与转化层交互时很有用。
$request
参数是当前被执行的HTTP请求,当你的转化层需要查询字符串参数或者其他相关数据时很有用。
这篇关于Laravel Lumen RESTFul API 扩展包:Dingo API(一) —— 安装配置篇的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!