laravel dingo Api结合jwt 处理token校验
阅读原文时间:2021年04月21日阅读:1

声明:

1.由于时间有限,本文有很多不足之处,望评论下方留言指正!

2.本文中代码仅做参考使用,不做实际项目运用,主要是思路,红色部分的注意项要留意!

3.篇幅较长,注意捡重点看,思路!思路!思路!


开拔~~~~~~


一、环境说明:

采用laravel5.8框架,php版本7.2.1

二、安装

两种方式,依赖包版本可根据自己实际情况进行调整

1.在项目目录下composer.json添加依赖包

#composer.json中

"require": { #…….
"dingo/api": "^2.0",
"tymon/jwt-auth": "~1.0.0-rc.1",
#……
}

2.直接require 依赖包

composer requiredingo/api 2.0 composer require tymon/jwt-auth 1.0.0-rc.1

三、发布配置文件

1.发布dingo配置文件

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

此命令会在 config 目录下生成一个 api.php 配置文件,你可以在此进行自定义配置。

env(‘API\_STANDARDS\_TREE‘, ‘x‘), /\* |-------------------------------------------------------------------------- | API Subtype |-------------------------------------------------------------------------- | | Your subtype will follow the standards tree you use when used in the | "Accept" header to negotiate the content type and version. | | For example: Accept: application/x.SUBTYPE.v1+json | \*/ ‘subtype‘ => env(‘API\_SUBTYPE‘, ‘‘), /\* |-------------------------------------------------------------------------- | Default API Version |-------------------------------------------------------------------------- | | This is the default version when strict mode is disabled and your API | is accessed via a web browser. It‘s also used as the default version | when generating your APIs documentation. | \*/ ‘version‘ => env(‘API\_VERSION‘, ‘v1‘), /\* |-------------------------------------------------------------------------- | Default API Prefix |-------------------------------------------------------------------------- | | A default prefix to use for your API routes so you don‘t have to | specify it for each group. | \*/ ‘prefix‘ => env(‘API\_PREFIX‘, ‘api‘), ‘paginate‘ => \[ ‘limit‘ => 15, \], /\* |-------------------------------------------------------------------------- | Default API Domain |-------------------------------------------------------------------------- | | A default domain to use for your API routes so you don‘t have to | specify it for each group. | \*/ ‘domain‘ => env(‘API\_DOMAIN‘, null), /\* |-------------------------------------------------------------------------- | Name |-------------------------------------------------------------------------- | | When documenting your API using the API Blueprint syntax you can | configure a default name to avoid having to manually specify | one when using the command. | \*/ ‘name‘ => env(‘API\_NAME‘, null), /\* |-------------------------------------------------------------------------- | Conditional Requests |-------------------------------------------------------------------------- | | Globally enable conditional requests so that an ETag header is added to | any successful response. Subsequent requests will perform a check and | will return a 304 Not Modified. This can also be enabled or disabled | on certain groups or routes. | \*/ ‘conditionalRequest‘ => env(‘API\_CONDITIONAL\_REQUEST‘, true), /\* |-------------------------------------------------------------------------- | Strict Mode |-------------------------------------------------------------------------- | | Enabling strict mode will require clients to send a valid Accept header | with every request. This also voids the default API version, meaning | your API will not be browsable via a web browser. | \*/ ‘strict‘ => env(‘API\_STRICT‘, false), /\* |-------------------------------------------------------------------------- | Debug Mode |-------------------------------------------------------------------------- | | Enabling debug mode will result in error responses caused by thrown | exceptions to have a "debug" key that will be populated with | more detailed information on the exception. | \*/ ‘debug‘ => env(‘API\_DEBUG‘, false), /\* |-------------------------------------------------------------------------- | Generic Error Format |-------------------------------------------------------------------------- | | When some HTTP exceptions are not caught and dealt with the API will | generate a generic error response in the format provided. Any | keys that aren‘t replaced with corresponding values will be | removed from the final response. | \*/ ‘errorFormat‘ => \[ ‘message‘ => ‘:message‘, ‘errors‘ => ‘:errors‘, ‘code‘ => ‘:code‘, ‘status\_code‘ => ‘:status\_code‘, ‘debug‘ => ‘:debug‘, \], /\* |-------------------------------------------------------------------------- | API Middleware |-------------------------------------------------------------------------- | | Middleware that will be applied globally to all API requests. | \*/ ‘middleware‘ => \[ \], /\* |-------------------------------------------------------------------------- | Authentication Providers |-------------------------------------------------------------------------- | | The authentication providers that should be used when attempting to | authenticate an incoming API request. | \*/ ‘auth‘ => \[ ‘jwt‘ => ‘Dingo\\Api\\Auth\\Provider\\JWT‘, \], /\* |-------------------------------------------------------------------------- | Throttling / Rate Limiting |-------------------------------------------------------------------------- | | Consumers of your API can be limited to the amount of requests they can | make. You can create your own throttles or simply change the default | throttles. | \*/ ‘throttling‘ => \[ \], /\* |-------------------------------------------------------------------------- | Response Transformer |-------------------------------------------------------------------------- | | Responses can be transformed so that they are easier to format. By | default a Fractal transformer will be used to transform any | responses prior to formatting. You can easily replace | this with your own transformer. | \*/ ‘transformer‘ => env(‘API\_TRANSFORMER‘, Dingo\\Api\\Transformer\\Adapter\\Fractal::class), /\* |-------------------------------------------------------------------------- | Response Formats |-------------------------------------------------------------------------- | | Responses can be returned in multiple formats by registering different | response formatters. You can also customize an existing response | formatter with a number of options to configure its output. | \*/ ‘defaultFormat‘ => env(‘API\_DEFAULT\_FORMAT‘, ‘json‘), ‘formats‘ => \[ //‘json‘ => Dingo\\Api\\Http\\Response\\Format\\Json::class, #json 返回自定义 ‘json‘ => App\\Components\\Response\\Format\\Json::class, \], ‘formatsOptions‘ => \[ ‘json‘ => \[ ‘pretty\_print‘ => env(‘API\_JSON\_FORMAT\_PRETTY\_PRINT\_ENABLED‘, false), ‘indent\_style‘ => env(‘API\_JSON\_FORMAT\_INDENT\_STYLE‘, ‘space‘), ‘indent\_size‘ => env(‘API\_JSON\_FORMAT\_INDENT\_SIZE‘, 2), \], \], /\* \* 接口频率限制 \*/ ‘rate\_limits‘ => \[ // 访问频率限制,次数/分钟 ‘access‘ => \[ ‘expires‘ => env(‘RATE\_LIMITS\_EXPIRES‘, 1), ‘limit‘ => env(‘RATE\_LIMITS‘, 600), \], // 登录相关,次数/分钟 ‘sign‘ => \[ ‘expires‘ => env(‘SIGN\_RATE\_LIMITS\_EXPIRES‘, 1), ‘limit‘ => env(‘SIGN\_RATE\_LIMITS‘, 10), \], \], \]; #### 2.发布jwt配置文件 php artisan vendor:publish --provider="Tymon\\JWTAuth\\Providers\\LaravelServiceProvider" 此命令会在 `config` 目录下生成一个 `jwt.php` 配置文件,你可以在此进行自定义配置。 jwt.php文件 \* \* For the full copyright and license information, please view the LICENSE \* file that was distributed with this source code. \*/ return \[ /\* |-------------------------------------------------------------------------- | JWT Authentication Secret |-------------------------------------------------------------------------- | | Don‘t forget to set this in your .env file, as it will be used to sign | your tokens. A helper command is provided for this: | \`php artisan jwt:secret\` | | Note: This will be used for Symmetric algorithms only (HMAC), | since RSA and ECDSA use a private/public key combo (See below). | | 加密生成 token 的 secret \*/ ‘secret‘ => env(‘JWT\_SECRET‘), /\* |-------------------------------------------------------------------------- | JWT Authentication Keys |-------------------------------------------------------------------------- | | The algorithm you are using, will determine whether your tokens are | signed with a random string (defined in \`JWT\_SECRET\`) or using the | following public & private keys. | | Symmetric Algorithms: | HS256, HS384 & HS512 will use \`JWT\_SECRET\`. | | Asymmetric Algorithms: | RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below. | | 如果你在 .env 文件中定义了 JWT\_SECRET 的随机字符串 | 那么 jwt 将会使用 对称算法 来生成 token | 如果你没有定有,那么jwt 将会使用如下配置的公钥和私钥来生成 token \*/ ‘keys‘ => \[ /\* |-------------------------------------------------------------------------- | Public Key |-------------------------------------------------------------------------- | | A path or resource to your public key. | | E.g. ‘file://path/to/public/key‘ | \*/ ‘public‘ => env(‘JWT\_PUBLIC\_KEY‘), /\* |-------------------------------------------------------------------------- | Private Key |-------------------------------------------------------------------------- | | A path or resource to your private key. | | E.g. ‘file://path/to/private/key‘ | \*/ ‘private‘ => env(‘JWT\_PRIVATE\_KEY‘), /\* |-------------------------------------------------------------------------- | Passphrase |-------------------------------------------------------------------------- | | The passphrase for your private key. Can be null if none set. | \*/ ‘passphrase‘ => env(‘JWT\_PASSPHRASE‘), \], /\* |-------------------------------------------------------------------------- | JWT time to live |-------------------------------------------------------------------------- | | Specify the length of time (in minutes) that the token will be valid for. | Defaults to 1 hour. | | You can also set this to null, to yield a never expiring token. | Some people may want this behaviour for e.g. a mobile app. | This is not particularly recommended, so make sure you have appropriate | systems in place to revoke the token if necessary. | Notice: If you set this to null you should remove ‘exp‘ element from ‘required\_claims‘ list. | 指定 access\_token 有效的时间长度(以分钟为单位),默认为1小时,您也可以将其设置为空,以产生永不过期的标记 \*/ ‘ttl‘ => env(‘JWT\_TTL‘, 60), /\* |-------------------------------------------------------------------------- | Refresh time to live |-------------------------------------------------------------------------- | | Specify the length of time (in minutes) that the token can be refreshed | within. I.E. The user can refresh their token within a 2 week window of | the original token being created until they must re-authenticate. | Defaults to 2 weeks. | | You can also set this to null, to yield an infinite refresh time. | Some may want this instead of never expiring tokens for e.g. a mobile app. | This is not particularly recommended, so make sure you have appropriate | systems in place to revoke the token if necessary. | | access\_token 可刷新的时间长度(以分钟为单位)。默认的时间为 2 周。 | 用法:如果用户有一个 access\_token,那么他可以带着他的 access\_token | 过来领取新的 access\_token,直到 2 周的时间后,他便无法继续刷新了,需要重新登录。 \*/ ‘refresh\_ttl‘ => env(‘JWT\_REFRESH\_TTL‘, 20160), /\* |-------------------------------------------------------------------------- | JWT hashing algorithm |-------------------------------------------------------------------------- | | Specify the hashing algorithm that will be used to sign the token. | | See here: https://github.com/namshi/jose/tree/master/src/Namshi/JOSE/Signer/OpenSSL | for possible values. | 指定将用于对令牌进行签名的散列算法。 \*/ ‘algo‘ => env(‘JWT\_ALGO‘, ‘HS256‘), /\* |-------------------------------------------------------------------------- | Required Claims |-------------------------------------------------------------------------- | | Specify the required claims that must exist in any token. | A TokenInvalidException will be thrown if any of these claims are not | present in the payload. | |指定必须存在于任何令牌中的声明。 \*/ ‘required\_claims‘ => \[ ‘iss‘, ‘iat‘, ‘exp‘, ‘nbf‘, ‘sub‘, ‘jti‘, \], /\* |-------------------------------------------------------------------------- | Persistent Claims |-------------------------------------------------------------------------- | | Specify the claim keys to be persisted when refreshing a token. | \`sub\` and \`iat\` will automatically be persisted, in | addition to the these claims. | | Note: If a claim does not exist then it will be ignored. | |指定在刷新令牌时要保留的声明密钥。 \*/ ‘persistent\_claims‘ => \[ // ‘foo‘, // ‘bar‘, \], /\* |-------------------------------------------------------------------------- | Lock Subject |-------------------------------------------------------------------------- | | This will determine whether a \`prv\` claim is automatically added to | the token. The purpose of this is to ensure that if you have multiple | authentication models e.g. \`App\\User\` & \`App\\OtherPerson\`, then we | should prevent one authentication request from impersonating another, | if 2 tokens happen to have the same id across the 2 different models. | | Under specific circumstances, you may want to disable this behaviour | e.g. if you only have one authentication model, then you would save | a little on token size. | | \*/ ‘lock\_subject‘ => true, /\* |-------------------------------------------------------------------------- | Leeway |-------------------------------------------------------------------------- | | This property gives the jwt timestamp claims some "leeway". | Meaning that if you have any unavoidable slight clock skew on | any of your servers then this will afford you some level of cushioning. | | This applies to the claims \`iat\`, \`nbf\` and \`exp\`. | | Specify in seconds - only if you know you need it. | \*/ ‘leeway‘ => env(‘JWT\_LEEWAY‘, 0), /\* |-------------------------------------------------------------------------- | Blacklist Enabled |-------------------------------------------------------------------------- | | In order to invalidate tokens, you must have the blacklist enabled. | If you do not want or need this functionality, then set this to false. | | 为了使令牌无效,您必须启用黑名单。如果不想或不需要此功能,请将其设置为 false。 \*/ ‘blacklist\_enabled‘ => env(‘JWT\_BLACKLIST\_ENABLED‘, true), /\* | ------------------------------------------------------------------------- | Blacklist Grace Period | ------------------------------------------------------------------------- | | When multiple concurrent requests are made with the same JWT, | it is possible that some of them fail, due to token regeneration | on every request. | | Set grace period in seconds to prevent parallel request failure. | | 当多个并发请求使用相同的JWT进行时,由于 access\_token 的刷新 ,其中一些可能会失败,以秒为单位设置请求时间以防止并发的请求失败。 \*/ ‘blacklist\_grace\_period‘ => env(‘JWT\_BLACKLIST\_GRACE\_PERIOD‘, 0), /\* |-------------------------------------------------------------------------- | Cookies encryption |-------------------------------------------------------------------------- | | By default Laravel encrypt cookies for security reason. | If you decide to not decrypt cookies, you will have to configure Laravel | to not encrypt your cookie token by adding its name into the $except | array available in the middleware "EncryptCookies" provided by Laravel. | see https://laravel.com/docs/master/responses#cookies-and-encryption | for details. | | Set it to true if you want to decrypt cookies. | \*/ ‘decrypt\_cookies‘ => false, /\* |-------------------------------------------------------------------------- | Providers |-------------------------------------------------------------------------- | | Specify the various providers used throughout the package. | | 指定整个包中使用的各种提供程序。 \*/ ‘providers‘ => \[ /\* |-------------------------------------------------------------------------- | JWT Provider |-------------------------------------------------------------------------- | | Specify the provider that is used to create and decode the tokens. | | 用于创建和解码令牌的提供程序。 \*/ ‘jwt‘ => Tymon\\JWTAuth\\Providers\\JWT\\Lcobucci::class, /\* |-------------------------------------------------------------------------- | Authentication Provider |-------------------------------------------------------------------------- | | Specify the provider that is used to authenticate users. | | 用于对用户进行身份验证的提供程序。 \*/ ‘auth‘ => Tymon\\JWTAuth\\Providers\\Auth\\Illuminate::class, /\* |-------------------------------------------------------------------------- | Storage Provider |-------------------------------------------------------------------------- | | Specify the provider that is used to store tokens in the blacklist. | | 用于在黑名单中存储标记的提供程序。 \*/ ‘storage‘ => Tymon\\JWTAuth\\Providers\\Storage\\Illuminate::class, \], \]; #### 3.jwt生成秘钥 jwt-auth已经预先定义好了一个 **Artisan** 命令方便你生成 Secret,通过下面命令生成 php artisan jwt:secret 该命令会在你的 `.env` 文件中新增一行 `JWT_SECRET=`secret,如下所示 #.env JWT\_SECRET=HSKxIUfwCdJj5ewdbqfQo5im9zj3r5g9 ####  4.注册中间件 JWT 认证扩展包附带了允许我们使用的中间件。在 app/Http/Kernel.php 中注册 auth.jwt 中间件: protected $routeMiddleware = \[ .... ‘auth.jwt‘ => \\Tymon\\JWTAuth\\Http\\Middleware\\Authenticate::class, \]; #### 5.设置路由,调整routes/api.php文件,和下方第“七、控制器创建” 对应 version(‘v1‘, function ($api) { $api\->get(‘demo‘, function () { return ‘hello world‘; }); }); # 示例2 $api\->version(‘v2‘, \[ ‘namespace‘ => ‘App\\Http\\Controllers\\Api\\V2‘, ‘middleware‘ => ‘serializer:array‘, \], function($api) { $api\->group(\[ ‘middleware‘ => \[‘api.throttle‘,‘global.log‘\], ‘limit‘ => config(‘api.rate\_limits.sign.limit‘),#接口访问限制 ‘expires‘ => config(‘api.rate\_limits.sign.expires‘), \], function($api){ # 无需校验token的接口 //...... $api\->post(‘login‘, ‘AuthController@login‘)->name(‘api.auth.login‘); // 需要 token 验证的接口 $api\->group(\[‘middleware‘ => \[‘auth.jwt‘\]\], function($api) { $api\->post(‘login‘, ‘AuthController@login‘)->name(‘api.auth.login‘); $api\->post(‘logout‘, ‘AuthController@logout‘)->name(‘api.auth.logout‘); $api\->post(‘refresh‘, ‘AuthController@refresh‘)->name(‘api.auth.refresh‘); $api\->post(‘me‘, ‘AuthController@me‘)->name(‘api.auth.me‘); //...... }); }); }); ?>

四、配置调整

1.添加配置(config/app.php)

‘providers‘ => [ ……

   Dingo\\Api\\Provider\\LaravelServiceProvider::class,

Tymon\JWTAuth\Providers\LaravelServiceProvider::class, ],

‘aliases‘ => [
……

  ‘JWTAuth‘ => Tymon\\JWTAuth\\Facades\\JWTAuth::class,  
  ‘JWTFactory‘ => Tymon\\JWTAuth\\Facades\\JWTFactory::class,

]

2.修改auth.php

‘guards‘ => [ ‘web‘ => [ ‘driver‘ => ‘session‘,
‘provider‘ => ‘users‘, ],

    ‘api‘ => \[ ‘driver‘ => ‘jwt‘,#把此处驱动改为jwt,默认为laravel框架自带的驱动token
        ‘provider‘ => ‘users‘,//注意此处根据自己的实际情况进行调整  

], ],

‘providers‘ => [
‘users‘ => [
‘driver‘ => ‘eloquent‘,
‘model‘ => App\Models\User::class,//注意此处根据自己的实际情况进行调整
],

// ‘users‘ => \[  
//     ‘driver‘ => ‘database‘,  
//     ‘table‘ => ‘users‘,  
// \],  

],

3 .env文件

#Dingo

# 公开的及商业项目用 vnd
API_STANDARDS_TREE=prs
#项目简称
API_SUBTYPE=dingo-demo
#域名
API_DOMAIN=
# 前缀
API_PREFIX=api
#不提供版本时使用的版本号
API_VERSION=v3
#开启 debug 模式
API_DEBUG=true
API_DEFAULT_FORMAT=json

#Jwt
#jwt秘钥
JWT_SECRET=HSKxIUfwCdJj5ewdbqfQo5im9zj3r5g9

#jwt有效时间,单位:分钟 JWT_TTL=60

五、更新 User 模型

使用默认的 User 表来生成 token

JWT 需要在 User 模型中实现 Tymon\JWTAuth\Contracts\JWTSubject 接口。 此接口需要实现两个方法  getJWTIdentifier 和 getJWTCustomClaims。使用以下内容更新 app/User.php 。

<?php

namespace App; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Tymon\JWTAuth\Contracts\JWTSubject; class User extends Authenticatable implements JWTSubject
{ use Notifiable; /**
* The attributes that are mass assignable.
*
* @var array */
protected $fillable = [ ‘name‘, ‘email‘, ‘password‘, ]; /**
* The attributes that should be hidden for arrays.
*
* @var array */
protected $hidden = [ ‘password‘, ‘remember_token‘, ]; /**
* Get the identifier that will be stored in the subject claim of the JWT.
*
* @return mixed */
public function getJWTIdentifier()
{ return $this->getKey();
} /**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* @return array */
public function getJWTCustomClaims()
{ return [];
}
}

六、JWT 身份验证逻辑

使用 JWT 身份验证在 laravel 中写 Restful API 的逻辑。

用户注册时需要姓名,邮箱和密码。那么,让我们创建一个表单请求来验证数据。通过运行以下命令创建名为AuthorizationRequest的表单请求:

php artisan make:request Api\AuthorizationRequest

它将在 app/Http/Requests/Api 目录下创建 AuthorizationRequest文件。

<?php

namespace App\Http\Requests\Api; class AuthorizationRequest extends Request
{ /**
* 确定是否授权用户发出此请求
*
* @return bool */
public function authorize()
{ return true;
} /**

 \* 获取应用于请求的验证规则
 \* Get the validation rules that apply to the request.
 \*
 \* @return array \*/
public function rules()
{ return \[ ‘username‘ => ‘required|string‘,
        ‘password‘ => ‘required|string|min:6‘, \];
}

}

七、控制器创建

官方案例,稍作了修改: login登录,me获取用户信息,logout退出登录,refresh刷新token,respondWithToken返回token

<?php
namespace App\Http\Controllers;

use use App\Http\Requests\Api\AuthorizationRequest; class AuthController extends Controller
{ /**
* Create a new AuthController instance.
* 要求附带email和password(数据来源users表)
* @return void */
public function __construct()
{ // 这里额外注意了:官方文档样例中只除外了『login』
// 这样的结果是,token 只能在有效期以内进行刷新,过期无法刷新
// 如果把 refresh 也放进去,token 即使过期但仍在刷新期以内也可刷新
// 不过刷新一次作废
$this->middleware(‘auth:api‘, [‘except‘ => [‘login‘]]); // 另外关于上面的中间件,官方文档写的是『auth:api』
// 但是我推荐用 『jwt.auth』,效果是一样的,但是有更加丰富的报错信息返回
} /**
* Get a JWT via given credentials.
* @param AuthorizationRequest $request
* @return \Illuminate\Http\JsonResponse */
public function login(AuthorizationRequest $request)
{ $credentials = request([‘email‘, ‘password‘]); if (! $token = auth(‘api‘)->attempt($credentials)) { return response()->json([‘error‘ => ‘Unauthorized‘], 401);
} return $this->respondWithToken($token);
} /**
* Get the authenticated User.
*
* @return \Illuminate\Http\JsonResponse */
public function me()
{ return response()->json(auth(‘api‘)->user());
} /**
* Log the user out (Invalidate the token).
*
* @return \Illuminate\Http\JsonResponse */
public function logout()
{
auth(‘api‘)->logout(); return response()->json([‘message‘ => ‘Successfully logged out‘]);
} /**
* Refresh a token.
* 刷新token,如果开启黑名单,以前的token便会失效。
* 值得注意的是用上面的getToken再获取一次Token并不算做刷新,两次获得的Token是并行的,即两个都可用。
* @return \Illuminate\Http\JsonResponse */
public function refresh()
{ return $this->respondWithToken(auth(‘api‘)->refresh());
} /**
* Get the token array structure.
*
* @param string $token
*
* @return \Illuminate\Http\JsonResponse */
protected function respondWithToken($token)
{ return response()->json([ ‘access_token‘ => $token,
‘token_type‘ => ‘bearer‘,
‘expires_in‘ => auth(‘api‘)->factory()->getTTL() * 60 ]);
}
}

注意:

jwt使用

jwt koken两种使用方式

1.加到 url 中:?token=你的token

2.加到 header 中,建议用这种,因为在 https 情况下更安全:Authorization:Bearer 你的token

八、自定义Dingo Api 响应格式

1.新建Json.php文件,App\Components\Response\Format\Json.php, 代码示例如下:

主要思路就是继承Dingo\Api\Http\Response\Format\Json类,并进行重写

<?php

namespace App\Components\Response\Format; use App\Components\Results\Code\SuccessCode; use Dingo\Api\Http\Response\Format\Json as DingoJson; class Json extends DingoJson
{ /**
* Encode the content to its JSON representation.
*
* @param mixed $content
*
* @return string */
protected function encode($content)
{ $jsonEncodeOptions = []; // Here is a place, where any available JSON encoding options, that
// deal with users‘ requirements to JSON response formatting and
// structure, can be conveniently applied to tweak the output.

    if ($this\->isJsonPrettyPrintEnabled()) { $jsonEncodeOptions\[\] = JSON\_PRETTY\_PRINT; $jsonEncodeOptions\[\] = JSON\_UNESCAPED\_UNICODE;
    }

    #主要在此处进行调整
    $newContent = \[ ‘code‘ => $content\[‘code‘\] ?? SuccessCode::SUCCESS,
        ‘data‘ => $content\[‘data‘\] ?? \[\],
        ‘message‘ => $content\[‘message‘\] ?? SuccessCode::SUCCESS\_MSG, \]; $encodedString = $this\->performJsonEncoding($newContent, $jsonEncodeOptions); if ($this\->isCustomIndentStyleRequired()) { $encodedString = $this\->indentPrettyPrintedJson( $encodedString,
            $this\->options\[‘indent\_style‘\]
        );
    } return $encodedString;
}

}

注意:由于自定义了响应返回,所以"七、控制器创建"的示例代码中需要调整格式为,需要有data键

/**
* 响应 Token 结构体
*
* @param $token
* @return mixed */
protected function respondWithToken($token)
{ $res = [ ‘data‘ => [ ‘token‘ => $token,
‘token_type‘ => ‘Bearer‘,
‘expires_in‘ => \Auth::guard(‘api‘)->factory()->getTTL()
]
]; return $this->response->array($res);
}

2.在config/api.php文件中,调整json返回类

#config/api.php
‘formats‘ => [ //‘json‘ => Dingo\Api\Http\Response\Format\Json::class,
#json 返回自定义
‘json‘ => App\Components\Response\Format\Json::class, ],

九、自定义Dingo 异常返回

1.新建API异常处理文件App\Exceptions\ApiHandler,具体实现根据自己需要,此处代码仅做参考,注意:文件里面有自定义的code码,另外该文件只是示例,可根据自己需要进行调整

<?php

namespace App\Exceptions; use App\Components\Results\Code\AuthCode; use App\Components\Results\Code\CommonCode; use App\Components\Results\Code\ErrorCode; use Exception; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Intervention\Image\Exception\NotFoundException; use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; use App\Components\Results\Exception\ServiceErrorException; use App\Components\Results\Exception\ServiceException; use App\Components\Results\Exception\ServiceLogicException; use App\Components\Results\Exception\ServiceValidException; use Tymon\JWTAuth\Exceptions\JWTException; use Tymon\JWTAuth\Exceptions\TokenBlacklistedException;
#该目录下面的几个文件,在下面有示例,可根据情况自行调整 use App\Components\Results\Results; class ApiHandler extends ExceptionHandler
{ /**
* A list of the exception types that are not reported.
*
* @var array */
protected $dontReport = [ //
]; /**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array */
protected $dontFlash = [ ‘password‘,
‘password_confirmation‘, ]; /**
* Report or log an exception.
*
* @param \Exception $exception
* @return void */
public function report(Exception $exception)
{
parent::report($exception);
} /**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \App\Components\Results\Result | \Illuminate\Http\Response
* @see https://learnku.com/docs/dingo-api/2.0.0/Errors-And-Error-Responses/1447 */
public function render($request, Exception $exception)
{
if ($request->isJson()) { $class = get_class($exception); switch ($class) { case ‘Dingo\Api\Exception\ValidationHttpException‘:
return Results::failure(AuthCode::MISSING_ACCESS_TOKEN_MSG, AuthCode::MISSING_ACCESS_TOKEN); case ‘Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException‘:
case ‘Tymon\JWTAuth\Exceptions\JWTException‘://Token could not be parsed from the request.
case ‘Tymon\JWTAuth\Exceptions\TokenBlacklistedException‘://The token has been blackliste
return Results::failure(AuthCode::MISSING_ACCESS_TOKEN_MSG, AuthCode::MISSING_ACCESS_TOKEN); case ‘Symfony\Component\HttpKernel\Exception\NotFoundHttpException‘:
return Results::failure(CommonCode::URL_NOT_FOUND_MSG, CommonCode::URL_NOT_FOUND); case ‘App\Components\Results\Exception\ServiceValidException‘:
case ‘App\Components\Results\Exception\ServiceLogicException‘:
case ‘App\Components\Results\Exception\ServiceErrorException‘:
case ‘App\Components\Results\Exception\ServiceException‘:
return Results::failure($exception->getMessage(), $exception->getErrorCode()); case ‘Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException‘:
return Results::failure($exception->getMessage(), $exception->getCode()); case ‘Dingo\Api\Exception\RateLimitExceededException‘:
return Results::failure(CommonCode::IRATE_LIMIT_REQUEST_MSG, CommonCode::RATE_LIMIT_REQUEST); default:
return Results::error(ErrorCode::UNKNOWN_ERROR_MSG, ErrorCode::UNKNOWN_ERROR);
}
} if(config(‘app.debug‘)){ return parent::render($request, $exception);
} if (method_exists($exception, ‘getStatusCode‘)) { $statusCode = $exception->getStatusCode(); switch ($statusCode) { case 400:
case 403:
case 405:
return response()->view(‘errors.404‘, [‘message‘=>$exception->getMessage()], $exception->getStatusCode()); break; case 500:
case 501:
case 502:
return response()->view(‘errors.500‘, [‘message‘=>$exception->getMessage()], $exception->getStatusCode()); break; default:
return response()->view(‘errors.404‘, [‘message‘=>$exception->getMessage()], $exception->getStatusCode()); break;
}
} return parent::render($request, $exception);
}

}

CommonCode.php代码示例:

<?php

namespace App\Components\Results\Code; /**
* 公共的业务异常错误码
* Class CommonCode
* @package App\Components\Results\Code */
class CommonCode
{ const INVALID_ARGS = "C_1"; const INVALID_ARGS_MSG = "参数无效"; const DATA_NOT_FOUND = "C_2"; const DATA_NOT_FOUND_MSG = "无数据"; //……
}

Results.php文件示例:

<?php

namespace App\Components\Results; use App\Components\Results\Code\CommonCode; use App\Components\Results\Code\ErrorCode; use App\Components\Results\Code\SuccessCode; final class Results
{ /**
* 成功
* @param mixed data 并设置data参数
* @param string $code 错误码
* @return Result */
public static function success($data = null,$code=SuccessCode::SUCCESS)
{ return new Result(SuccessCode::SUCCESS_MSG, $code, $data);
}

/**
* 失败
* @param string $message 失败信息
* @param string $code 失败编码
* @return Result 返回对象
*/
public static function failure(string $message, string $code) {
return new Result($message, $code);
}

}

Result.php文件示例

<?php

namespace App\Components\Results; use App\Components\Results\Code\ErrorCode; use App\Components\Results\Code\SuccessCode; use App\Components\Results\Code\ErrorCode; /**
* Class Result */
class Result
{ public $code; public $message; public $data; public function __construct($message, $code, $data = null)
{ $this->message = $message; $this->code = $code; $this->data = $data;
} /**
* 获取错误码
* @return string 错误码 */
function getCode(): string { return $this->code;
} /**
* 获取成功或错误的信息
* @return string 成功或错误的信息 */
function getMessage(): string { return $this->message;
} /**
* 获取数据
* @return object 数据 */
function getData()
{ return $this->data;
} /**
* 设置错误码
* @param string code 错误码
* @return Result Result对象 */
function setCode(string $code): Result
{ $this->code = $code; return $this;
} /**
* 设置成功或错误的信息
* @param string message 成功或错误的信息
* @return Result */
function setMessage(string $message): Result
{ $this->message = $message; return $this;
} /**
* 设置数据
* @param mixed data 数据
* @return Result */
function setData($data): Result
{ $this->data = $data; return $this;
} function __toString()
{ return json_encode($this, Constants::DEFAULT_ENCODING_OPTIONS | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
} function jsonSerialize()
{ return $this;
}
}

 2.在 App/Providers/AppServiceProvider中注册新的异常处理

<?php

namespace App\Providers; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider
{ /**
* Bootstrap any application services.
*
* @return void */
public function boot()
{
app(‘api.exception‘)->register(function (\Exception $exception) { $request = \Illuminate\Http\Request::capture(); return app(‘App\Exceptions\ApiHandler‘)->render($request, $exception);
});
} /**
* Register any application services.
*
* @return void */
public function register()
{ //……
}

}

参考文档:

https://learnku.com/docs/dingo-api/2.0.0/Configuration/1444#6cdca8

https://learnku.com/docs/laravel/5.8/api-authentication/3952

https://learnku.com/laravel/t/27760