首页 > 其他分享 >dingo api passport

dingo api passport

时间:2023-02-28 13:55:30浏览次数:32  
标签:task return dingo Dingo api API passport Api

配置阿里源

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

创建项目

composer create-project --prefer-dist laravel/laravel=6.* haoranapi

ide工具

composer require barryvdh/laravel-ide-helper --dev

# config/app中添加 Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,

# terminal运行

php artisan ide-helper:generate

debug

composer require barryvdh/laravel-debugbar --dev

# config/app中添加
Barryvdh\Debugbar\ServiceProvider::class,

解决跨域

composer require barryvdh/laravel-cors

protected $middleware = [
    // ...
    \Fruitcake\Cors\HandleCors::class,
];

php artisan vendor:publish --tag="cors"


安装dingo

"require": {
    "dingo/api": "^2.2"
}

composer update

php artisan vendor:publish --provider="Dingo\Api\Provider\LaravelServiceProvider"

安装passport

composer require laravel/passport

数据迁移

php artisan migrate

生成安全访问令牌时所需的加密密钥

php artisan passport:install


# 上面命令执行后,请将 Laravel\Passport\HasApiTokens Trait 添加到 App\User 模型
# 接下来,在 AuthServiceProvider 的 boot 方法中调用 Passport::routes 函数
# 最后,将配置文件 config/auth.php 中授权看守器 guards 的 api 的 driver 选项改为 passport

config/api.php说明

#接口围绕:[x]本地和私有环境 [prs]公司内部app使用 [vnd]公开接口
'standardsTree' => env('API_STANDARDS_TREE', 'x')

#项目名称
'subtype' => env('API_SUBTYPE', 'laradmin')

#Api前缀 通过 www.yiqiesuifeng.cn/api 来访问 API。
'prefix' => env('API_PREFIX', 'api')

#api域名
'domain' => env('API_DOMAIN', 'api.yiqiesuifeng.com'),

#版本号
'version' => env('API_VERSION', 'v1')

#开发时开启DEBUG便于发现错误
'debug' => env('API_DEBUG', false)

### prefix 与 domain 只能二选一,一个需要设置为null

### 响应数据格式高级的格式配置需要发布配置文件到 config 目录或者在服务提供者(Laravel)
Dingo\Api\Http\Response::addFormatter('json', new Dingo\Api\Http\Response\Format\Jsonp);
### 响应转化器Dingo 默认使用 Fractal 作为响应转化器
'transformer' => env('API_TRANSFORMER', Dingo\Api\Transformer\Adapter\Fractal::class),

### 你可以在 .env 中配置,但如果要实现更加复杂的配置还是需要在服务提供者(Laravel)或启动文件(Lumen)中操作
$app['Dingo\Api\Transformer\Factory']->setAdapter(function ($app) {
    $fractal = new League\Fractal\Manager;

    $fractal->setSerializer(new League\Fractal\Serializer\JsonApiSerializer);

    return new Dingo\Api\Transformer\Adapter\Fractal($fractal);
});

### 访问频率限制默认访问频率限制是被禁用的,你可以注册自定义的带有频率限制的节流器或者使用已经存在的认证/取消认证节流器。要进行更加复杂的配置,同样需要在服务提供者(Laravel)或启动文件(Lumen)中操作:
$app['Dingo\Api\Http\RateLimit\Handler']->extend(function ($app) {
    return new Dingo\Api\Http\RateLimit\Throttle\Authenticated;
});

### 认证提供者Dingo 默认只开启了 basic 认证,如果要对认证进行更复杂的配置,需要在服务提供者(Laravel)或启动文件(Lumen)设置
$app['Dingo\Api\Auth\Auth']->extend('oauth', function ($app) {
   return new Dingo\Api\Auth\Provider\JWT($app['Tymon\JWTAuth\JWTAuth']);
});

.env设置示例

#dingoapi
# 标准树 x 表示未注册树,主要用于本地或私有环境 prs 表示个人树,主要用于非商业性质的项目 vnd 表示供应商树,主要用于公开的以及商业性质的项目
API_STANDARDS_TREE=vnd
# 子类型 通常是应用或项目的简称
API_SUBTYPE=haoranapi
# 前缀和子域名
API_PREFIX=api
#API_DOMAIN=api.myapp.com
# 名字 使用 Dingo 提供的 API Blueprint 命令生成文档
API_NAME="docs_api"
# 带条件的请求 由于缓存 API 请求的时候会使用客户端缓存功能,所以默认开启了带条件的请求
API_CONDITIONAL_REQUEST=false
# 版本 API 的默认版本
API_VERSION=v1
# 调试模式
API_DEBUG=true
# Strict 模式 Strict 模式要求客户端发送 Accept 请求头而不是默认在配置文件中指定的版本,这意味着你不能通过 Web 浏览器访问 API
API_STRICT=false
# 响应数据格式
API_DEFAULT_FORMAT=json

使用

$api = app(\Dingo\Api\Routing\Router::class);

#默认配置指定的是v1版本,可以直接通过 {host}/api/version 访问到
$api->version('v1', function ($api) {
    $api->get('version', function () {
        return 'v1';
    });
});


#如果v2不是默认版本,需要设置请求头  
#Accept: application/[配置项standardsTree].[配置项subtype].v2+json
#Accept: application/x.laradmin.v2+json
$api->version('v2', function ($api) {
    $api->get('version', function () {
        return 'v2';
    });
});

$api->version('v1', function ($api) {
    $api->group(['middleware' => 'foo'], function ($api) {
        // Endpoints registered here will have the "foo" middleware applied.
    });
});

命名路由 & 生成 URL

$api->get('test', function(){
    return $url = app(\Dingo\Api\Routing\UrlGenerator::class)
        ->version('v1')
        ->route('login', ['id' => 1]);
});

## http://haoranapi.com/api/login?id=1

User模型 通过给定的username获取用户实例

public function findForPassport($username){
    return $this->where('name', $username)->first();
}

创建基类控制器 响应构建器

php artisan make:controller ApiController

# 引入trait 
use Helpers;

创建passport控制器

php artisan make:controller Api\PassportController

序列化器

Fractal 默认支持 ArraySerializer、DataArraySerializer、JsonApiSerializer 三种序列化器

创建转化器

# app/Transformers

<?php

namespace App\Transformers;

use App\Models\Housing;
use League\Fractal\TransformerAbstract;

class HousingTransformer extends TransformerAbstract
{
    public function transform(Housing $housing)
    {
        return [
            'id'         => $housing->id,
            'number'     => $housing->number,
            'acreage'    => $housing->acreage,
            'rent'       => $housing->rent,
            'deposit'    => $housing->deposit,
            'pay_num'    => $housing->pay_num,
            'pledge_num' => $housing->pledge_num,
            'ele_num'    => $housing->ele_num,
            'water_num'  => $housing->water_num,
            'remark'     => $housing->remark,
            'created_at' => $housing->created_at,
            'updated_at' => $housing->updated_at,
        ];
    }
    
        //访问关联数据,请求路由http://haoranapi.com/api/zuke?include=renters,housings
    //$defaultIncludes 所有此转换器自动加上关联
//    protected $defaultIncludes = ['housings','renters'];
    protected  $availableIncludes = ['housings','renters'];

    public function includeHousings(Bill $bill)
    {
        return $this->item($bill->housings, new HousingTransformer());
    }

    public function includeRenters(Bill $bill)
    {
        return $this->item($bill->renters, new RenterTransformer());
    }

}

单个资源响应

    public function show($id)
    {
        $detail = Housing::find($id);
        return $this->response->item($detail, HousingTransformer::class);
    }

资源集合响应

    public function index()
    {
        $housings = Housing::all();
        return $this->response->collection($housings, HousingTransformer::class);

    }

分页响应

    public function index()
    {
        $housings = Housing::paginate();
        return $this->response->paginator($housings, HousingTransformer::class);
    }

使用游标

    public function index()
    {
        $current = Request('current');
        $previous = Request('previous');
        $limit = Request('limit') ? : 10;

        if ($current) {
            $housings = Housing::where('id', '>', $current)->take($limit)->get();
        } else {
            $housings = Housing::take($limit)->get();
        }

        $next = $housings->last()->id;
        $cursor = new Cursor($current, $previous, $next, $housings->count());

        return $this->response->collection($housings, new HousingTransformer, [], function ($resource, $fractal) use ($cursor) {
            $resource->setCursor($cursor);
        });
    }

添加额外的响应头

return $this->response->item($task, new TaskTransformer)->withHeader('Foo', 'Bar');

return $this->response->item($task, new TaskTransformer())->withHeaders([
    'Foo' => 'Bar',
    'Hello' => 'World'
]);

# 添加 Cookie
$cookie = new \Symfony\Component\HttpFoundation\Cookie('foo', 'bar');
return $this->response->item($task, new TaskTransformer())->withCookie($cookie);

设置响应状态码

return $this->response->item($task, new TaskTransformer)->setStatusCode(200);

添加元数据

return $this->response->item($task, new TaskTransformer)->addMeta('foo', 'bar');

资源键

return $this->item($task, new TaskTransformer, ['key' => 'task']);

使用回调

// 引入命名空间
use League\Fractal\Serializer\JsonApiSerializer;

$task = Task::findOrFail($id);
return $this->response->item($task, new TaskTransformer(), ['key' => 'task'], function ($resource, $fractal) {
        $fractal->setSerializer(new JsonApiSerializer());
    });

Morphing 和 Morphed 事件 Dingo 提供的 ResponseIsMorphing(转化前触发)和 ResponseWasMorphed(转化后触发)事件。

# 我们可以在 app/Listeners 目录下为上述事件创建监听器

php artisan make:listener AddPaginationLinksToResponse

# 编辑 app/Listeners/AddPaginationLinksToResponse.php

namespace App\Listeners;

use Dingo\Api\Event\ResponseWasMorphed;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class AddPaginationLinksToResponse
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  object  $event
     * @return void
     */
    public function handle(ResponseWasMorphed $event)
    {
        if (isset($event->content['meta']['pagination'])) {
            $links = $event->content['meta']['pagination']['links'];
            $next = isset($links['next']) ? $links['next'] : null;
            $previous = isset($links['previous']) ? $links['previous'] : null;
            $event->response->headers->set(
                'link',
                sprintf('<%s>; rel="next", <%s>; rel="prev"', $next, $previous)
            );
        }
    }
}


# 然后通过在 EventServiceProvider 中注册事件及其对应监听器之间的映射关系来监听该事件

use App\Listeners\AddPaginationLinksToResponse;
use Dingo\Api\Event\ResponseWasMorphed;

protected $listen = [
    ...
    ResponseWasMorphed::class => [
        AddPaginationLinksToResponse::class
    ]
];


错误及异常处理

异常	状态码
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

# 资源异常
Dingo\Api\Exception\DeleteResourceFailedException
Dingo\Api\Exception\ResourceException
Dingo\Api\Exception\StoreResourceFailedException
Dingo\Api\Exception\UpdateResourceFailedException

# 自定义 HTTP 异常
除了 Dingo 内置支持的异常类之外,你可以创建自定义的 HTTP 异常,前提是它们继承自Symfony\Component\HttpKernel\Exception\HttpException 基类或者实现了Symfony\Component\HttpKernel\Exception\HttpExceptionInterface 接口。


表单请求类

如果你要使用表单请求类,那么需要继承 Dingo API 表单请求基类 Dingo\Api\Http\FormRequest 或者完全自己实现。表单请求类会检查输入请求是否是针对当前 API 的,如果是的话,当验证失败会抛出 Dingo\Api\Exception\ValidationHttpException 异常。这个异常会被 Dingo API 渲染并返回相应的错误响应。

应用内部请求 Dingo API

在 API 路由中请求 API

$api->version('v3', function ($api) {
    ...
    $api->get('task/{id}', function ($id) {
        $dispatcher = app(\Dingo\Api\Dispatcher::class);
        $task = $dispatcher->version('v1')->get('dingoapi/task/' . $id);
        dd($task);
    });
});

在 Web 路由中请求 API

$dispatcher = app(\Dingo\Api\Dispatcher::class);
Route::get('/task/{id}', function ($id) use ($dispatcher) {
    $task = $dispatcher->version('v3')->get('dingoapi/task/' . $id);
    dd($task);
});

在已认证 API 路由中请求

在已认证的 API 路由中请求 Dingo API 的认证接口比较简单,不需要做任何额外的操作,因为已经认证的 API 接口中发起内部请求中会自动带上 Authorization 字段。

在未认证或 Web 路由中请求

在未认证 API 路由或 Web 路由中对 Dingo API 认证接口发起内部请求需要将访问令牌设置到请求头 Authorization 字段中才可以,我们以之前待办任务列表中的 home 路由为例,这是一个 Web 路由,虽然需要认证才能访问,但是 Web 路由认证和 API 路由认证走的是两套体系,之间并不相通,内部请求 的 API 接口需要单独认证,这里我们借助 Passport 私人访问令牌来实现该 API 接口的认证,打开 app/Http/Controllers/HomeController.php,将 index 方法中获取任务列表的逻辑改为基于 Dingo API v3 版本的 tasks.index 接口获取,该接口是需要认证的,我们通过 Passport 私人访问令牌生成访问令牌,然后将其设置到 Authorization 请求头

public function index(Request $request, Dispatcher $dispatcher)
{
    $params = [];
    if ($request->has('page')) {
        $params['page'] = $request->get('page');
    }
    if ($request->has('limit')) {
        $params['limit'] = $request->get('limit');
    }
    $token = $request->user()->createToken('Internal Request Token')->accessToken;
    $tasks = $dispatcher->on('todo.test')->header('Authorization', 'Bearer ' . $token)->version('v3')->get('dingoapi/tasks', $params);
    return view('home', ['tasks' => json_encode($tasks->items())]);
}

发起 POST 请求

$dispatcher->with(['name' => '学院君', 'location' => '杭州'])->post('users');
$dispatcher->post('users', ['name' => '学院君', 'location' => '杭州']);

上传文件

$dispatcher->attach(Input::files())->post('photos');

$dispatcher->attach(['photo' => 'photos/me.jpg'])->post('photos');

$dispatcher->attach([
    'photo' => [
        'path' => 'photos/me.jpg',
        'mime' => 'image/jpeg',
        'size' => '49430'
    ]
])->post('photos');

发送 JSON 格式请求数据

$data = ['name' => 'xueyuanjun', 'password' => '12345678'];
$dispatcher->json($data)->post('users');

获取原生响应对象

$response = $dispatcher->raw()->get('users');


异常及错误处理

try {
    app(\Dingo\Api\Dispatcher::class)->with($payload)->post('dingoapi/tasks');
} catch (Symfony\Component\HttpKernel\Exception\ConflictHttpException $e) {
    // Do something here, like return with an error.
}

生成 API 文档

# Resource 

/**
 * Task Resource Controller
 * @package App\Http\Controllers\Api
 * @Resource("Tasks")
 */
 
 @Resource("Tasks", uri="/tasks")
 
 
 # Action
 /**
 * Display a listing of the resource.
 * @param Request $request
 * @return \Illuminate\Http\Response
 * @GET("/{?page,limit}"
 */
 
 /**
 * Store a newly created resource in storage.
 *
 * @param  CreateTaskRequest $request
 * @return \Illuminate\Http\Response
 * @POST("/")
 */
 
 # Version
 /**
 * Display a listing of the resource.
 * @param Request $request
 * @return \Illuminate\Http\Response
 * @GET("/{?page,limit}"
 * @Versions({"v3"})
 */
 
 
 # Parameter
 /**
 * Display a listing of the resource.
 * @param Request $request
 * @return \Illuminate\Http\Response
 * @GET("/{?page,limit}")
 * @Versions({"v3"})
 * @Parameters({
 *     @Parameter("page", description="page number", type="integer", required=false, default=1),
 *     @Parameter("limit", description="task item number per page", type="integer", required=false, default=10)
 * })
 */
 
 # Request
 /**
 * Store a newly created resource in storage.
 *
 * @param  CreateTaskRequest $request
 * @return \Illuminate\Http\Response
 * @POST("/")
 * @Versions({"v3"})
 * @Request("text=test&is_completed=1", contentType="application/x-www-form-urlencoded")
 */
 
 @Request("text={text}&is_completed={is_completed}", contentType="application/x-www-form-urlencoded", attributes={
    @Attribute("text", type="string", required=true, description="the body of task", sample="test task"),
    @Attribute("is_completed", type="boolean", required=true, description="task is completed or not", sample=1)
})

@Request({"text":"test task", "is_completed":1}, attributes={
    @Attribute("text", type="string", required=true, description="the body of task", sample="test task"),
    @Attribute("is_completed", type="boolean", required=true, description="task is completed or not", sample=1)
})

@Request({"text":"test task", "is_completed":0}, headers={
    "Authorization": "Bearer {API Access Token}"
}, attributes={
    @Attribute("text", type="string", required=true, description="the body of task", sample="test task"),
    @Attribute("is_completed", type="boolean", required=false, description="task is completed or not", sample=0)
})
 
 # Response
 /**
 * Store a newly created resource in storage.
 *
 * @param  CreateTaskRequest $request
 * @return \Illuminate\Http\Response
 * @POST("/")
 * @Versions({"v3"})
 * @Request({"text":"test task", "is_completed":0}, headers={
 *     "Authorization": "Bearer {API Access Token}"
 * }, attributes={
 *     @Attribute("text", type="string", required=true, description="the body of task", sample="test task"),
 *     @Attribute("is_completed", type="boolean", required=true, description="task is completed or not", sample=0)
 * })
 * @Response(200, body={"data":{"id":4,"text":"Test Task 4","completed":"no","link":"http://todo.test/dingoapi/task/4"}})
 */
 
 @Response(200, body={"data":{"id":1,"text":"Test Task 1","completed":"no","link":"http://todo.test/dingoapi/task/1"}}, attributes={
    @Attribute("id", type="integer", description="the id of task", sample=1),
    @Attribute("text", type="string", description="the body of task", sample="test task"),
    @Attribute("completed", type="string", description="task is completed or not", sample="no"),
    @Attribute("link", type="string", description="task link", sample="http://todo.test/dingoapi/task/1")
})

# Transaction
/**
 * Update the specified resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  int  $id
 * @return \Illuminate\Http\Response
 * @POST("/{id}")
 * @Versions({"v3"})
 * @Parameters({
 *     @Parameter("id", type="integer", description="the ID of the task", required=true)
 * })
 * @Transaction({
 *     @Request({"text":"test task", "is_completed":1}, headers={
 *         "Authorization": "Bearer {API Access Token}"
 *     }, attributes={
 *         @Attribute("text", type="string", required=true, description="the body of task", sample="test task"),
 *         @Attribute("is_completed", type="boolean", required=true, description="task is completed or not", sample=1)
 *     }),
 *     @Response(200, body={"data":{"id":1,"text":"Test Task 1","completed":"no","link":"http://todo.test/dingoapi/task/1"}}, attributes={
 *         @Attribute("id", type="integer", description="the id of task", sample=1),
 *         @Attribute("text", type="string", description="the body of task", sample="test task"),
 *         @Attribute("completed", type="string", description="task is completed or not", sample="no"),
 *         @Attribute("link", type="string", description="task link", sample="http://todo.test/dingoapi/task/1")
 *     }),
 *     @Response(404, body={"message":"404 not found", "status_code": 404})
 * })
 */
 

生成 API 文档

php artisan api:docs --name TodoApp --output-file apidocs.md

参考文档

https://xueyuanjun.com/post/19686

https://learnku.com/docs/dingo-api/2.0.0/Errors-And-Error-Responses/1447

https://learnku.com/docs/laravel/6.x/passport/5152#defining-scopes

标签:task,return,dingo,Dingo,api,API,passport,Api
From: https://www.cnblogs.com/fuqian/p/17163968.html

相关文章

  • 10. Laravel api 认证
    Laravelapi认证配套视频地址:https://www.bilibili.com/video/av74879198?p=3原理注册:用户注册成功后,随机生成长字符串作为token,原生token返回给用户。哈希后的......
  • 12. Laravel Passport 授权码模式
    LaravelPassport授权码模式配套视频地址:https://www.bilibili.com/video/av74879198?p=7哔哩哔哩提供一个“微信登陆”的链接,用户点击跳转到微信授权服务器。用户......
  • 11. Laravel Passport 密码模式
    LaravelPassport密码模式配套视频地址:https://www.bilibili.com/video/av74879198?p=5准备工作composercreate-project--prefer-distlaravel/laravellaravel6.......
  • 使用MetaWeblog api自动上传文章图片到cnblog
    由于我日常习惯用Typora来写笔记和博客,文章中的图片保存在本地,在发布文章到cnblog之前,希望能有一个自动化工具可以自动上传文章引用的图片到cnblog,获取图片链接,替换掉......
  • IDEA插件Apifox,一键自动生成接口文档!
    有关Apifox软件之前写过一篇文章:接口测试神器Apifox,亲测好用!如何一键自动生成数据库文档之前也写过一篇文章:数据库界的Swagger:一键生成数据库文档!一、Apifox插件的优......
  • 百度地图的API接口
     API接口API:应用程序接口(API:ApplicationProgramInterface)python为了在团队内部形成共识、防止个人习惯差异引起的混乱,我们需要找到一种大家都觉得很好的接口实现规......
  • 【Azure 存储服务】使用REST API操作Azure Storage Table,删除数据(Delete Entity)
    问题描述使用AzureStorageTable的RESTAPI,实现根据过滤条件删除满足条件的数据,调用方法为  DeleteEntity(AzureStorage) 问题实现第一步:通过AzureStroage门......
  • api网关的使用,实现访问控制
    文章目录​​场景​​​​什么是api网关,有什么用​​​​效果​​​​接入过程​​​​如何查看哪些人订阅了​​​​其他​​​​api和应用之间的关系​​​​api网关不要......
  • 使用npm包API Promise化
             ......
  • Flow API搭建指南
    搭建FlowAPI,首先需要安装知行之桥EDI系统,注意,FlowAPI为新增功能,仅在2022版本(8336)及以后支持,如果你发现正在使用的产品没有这个功能,可以在我们官网下载最新版本或者联系我......