声明:
1.由于时间有限,本文有很多不足之处,望评论下方留言指正!
2.本文中代码仅做参考使用,不做实际项目运用,主要是思路,红色部分的注意项要留意!
3.篇幅较长,注意捡重点看,思路!思路!思路!
开拔~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
一、环境说明:
采用laravel5.8框架,php版本7.2.1
二、安装
两种方式,依赖包版本可根据自己实际情况进行调整
1.在项目目录下composer.json添加依赖包
代码语言:javascript复制#composer.json中
"require": {
#.......
"dingo/api": "^2.0",
"tymon/jwt-auth": "~1.0.0-rc.1",
#......
}
2.直接require 依赖包
代码语言:javascript复制composer requiredingo/api 2.0
composer require tymon/jwt-auth 1.0.0-rc.1
三、发布配置文件
1.发布dingo配置文件
代码语言:javascript复制php artisan vendor:publish --provider="DingoApiProviderLaravelServiceProvider"
此命令会在 config
目录下生成一个 api.php
配置文件,你可以在此进行自定义配置。
<?php
return [
/*
|--------------------------------------------------------------------------
| Standards Tree
|--------------------------------------------------------------------------
|
| Versioning an API with Dingo revolves around content negotiation and
| custom MIME types. A custom type will belong to one of three
| standards trees, the Vendor tree (vnd), the Personal tree
| (prs), and the Unregistered tree (x).
|
| By default the Unregistered tree (x) is used, however, should you wish
| to you can register your type with the IANA. For more details:
| https://tools.ietf.org/html/rfc6838
|
*/
'standardsTree' => 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' => 'DingoApiAuthProviderJWT',
],
/*
|--------------------------------------------------------------------------
| 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', DingoApiTransformerAdapterFractal::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' => DingoApiHttpResponseFormatJson::class,
#json 返回自定义
'json' => AppComponentsResponseFormatJson::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配置文件
代码语言:javascript复制php artisan vendor:publish --provider="TymonJWTAuthProvidersLaravelServiceProvider"
此命令会在 config
目录下生成一个 jwt.php
配置文件,你可以在此进行自定义配置。
jwt.php文件
代码语言:javascript复制<?php
/*
* This file is part of jwt-auth.
*
* (c) Sean Tymon <tymon148@gmail.com>
*
* 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. `AppUser` & `AppOtherPerson`, 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' => TymonJWTAuthProvidersJWTLcobucci::class,
/*
|--------------------------------------------------------------------------
| Authentication Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to authenticate users.
|
| 用于对用户进行身份验证的提供程序。
*/
'auth' => TymonJWTAuthProvidersAuthIlluminate::class,
/*
|--------------------------------------------------------------------------
| Storage Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to store tokens in the blacklist.
|
| 用于在黑名单中存储标记的提供程序。
*/
'storage' => TymonJWTAuthProvidersStorageIlluminate::class,
],
];
3.jwt生成秘钥
jwt-auth已经预先定义好了一个 Artisan 命令方便你生成 Secret,通过下面命令生成
代码语言:javascript复制php artisan jwt:secret
该命令会在你的 .env
文件中新增一行 JWT_SECRET=
secret,如下所示
#.env
JWT_SECRET=HSKxIUfwCdJj5ewdbqfQo5im9zj3r5g9
4.注册中间件
JWT 认证扩展包附带了允许我们使用的中间件。在 app/Http/Kernel.php 中注册 auth.jwt 中间件:
代码语言:javascript复制protected $routeMiddleware = [
....
'auth.jwt' => TymonJWTAuthHttpMiddlewareAuthenticate::class,
];
5.设置路由,调整routes/api.php文件,和下方第“七、控制器创建” 对应
代码语言:javascript复制<?php
$api = app('DingoApiRoutingRouter');
# 示例1
$api->version('v1', function ($api) {
$api->get('demo', function () {
return 'hello world';
});
});
# 示例2
$api->version('v2', [
'namespace' => 'AppHttpControllersApiV2',
'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)
代码语言:javascript复制'providers' => [
......
2.修改auth.php
代码语言:javascript复制'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',#把此处驱动改为jwt,默认为laravel框架自带的驱动token
'provider' => 'users',//注意此处根据自己的实际情况进行调整
],
],
3 .env文件
代码语言:javascript复制#Dingo
五、更新 User 模型
使用默认的 User 表来生成 token
JWT 需要在 User 模型中实现 TymonJWTAuthContractsJWTSubject 接口。 此接口需要实现两个方法 getJWTIdentifier 和 getJWTCustomClaims。使用以下内容更新 app/User.php 。
代码语言:javascript复制<?php
namespace App;
use IlluminateFoundationAuthUser as Authenticatable;
use IlluminateNotificationsNotifiable;
use TymonJWTAuthContractsJWTSubject;
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的表单请求:
代码语言:javascript复制php artisan make:request ApiAuthorizationRequest
它将在 app/Http/Requests/Api 目录下创建 AuthorizationRequest文件。
代码语言:javascript复制<?php
namespace AppHttpRequestsApi;
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
代码语言:javascript复制<?php
namespace AppHttpControllers;
use use AppHttpRequestsApiAuthorizationRequest;
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 IlluminateHttpJsonResponse
*/
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 IlluminateHttpJsonResponse
*/
public function me()
{
return response()->json(auth('api')->user());
}
/**
* Log the user out (Invalidate the token).
*
* @return IlluminateHttpJsonResponse
*/
public function logout()
{
auth('api')->logout();
return response()->json(['message' => 'Successfully logged out']);
}
/**
* Refresh a token.
* 刷新token,如果开启黑名单,以前的token便会失效。
* 值得注意的是用上面的getToken再获取一次Token并不算做刷新,两次获得的Token是并行的,即两个都可用。
* @return IlluminateHttpJsonResponse
*/
public function refresh()
{
return $this->respondWithToken(auth('api')->refresh());
}
/**
* Get the token array structure.
*
* @param string $token
*
* @return IlluminateHttpJsonResponse
*/
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文件,AppComponentsResponseFormatJson.php, 代码示例如下:
主要思路就是继承DingoApiHttpResponseFormatJson类,并进行重写
代码语言:javascript复制<?php
namespace AppComponentsResponseFormat;
use AppComponentsResultsCodeSuccessCode;
use DingoApiHttpResponseFormatJson 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键
代码语言:javascript复制 /**
* 响应 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返回类
代码语言:javascript复制#config/api.php
'formats' => [
//'json' => DingoApiHttpResponseFormatJson::class,
#json 返回自定义
'json' => AppComponentsResponseFormatJson::class,
],
九、自定义Dingo 异常返回
1.新建API异常处理文件AppExceptionsApiHandler,具体实现根据自己需要,此处代码仅做参考,注意:文件里面有自定义的code码,另外该文件只是示例,可根据自己需要进行调整
代码语言:javascript复制<?php
namespace AppExceptions;
use AppComponentsResultsCodeAuthCode;
use AppComponentsResultsCodeCommonCode;
use AppComponentsResultsCodeErrorCode;
use Exception;
use IlluminateFoundationExceptionsHandler as ExceptionHandler;
use InterventionImageExceptionNotFoundException;
use SymfonyComponentHttpKernelExceptionUnauthorizedHttpException;
use SymfonyComponentHttpKernelExceptionMethodNotAllowedHttpException;
use AppComponentsResultsExceptionServiceErrorException;
use AppComponentsResultsExceptionServiceException;
use AppComponentsResultsExceptionServiceLogicException;
use AppComponentsResultsExceptionServiceValidException;
use TymonJWTAuthExceptionsJWTException;
use TymonJWTAuthExceptionsTokenBlacklistedException;
#该目录下面的几个文件,在下面有示例,可根据情况自行调整
use AppComponentsResultsResults;
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 IlluminateHttpRequest $request
* @param Exception $exception
* @return AppComponentsResultsResult | IlluminateHttpResponse
* @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 'DingoApiExceptionValidationHttpException':
return Results::failure(AuthCode::MISSING_ACCESS_TOKEN_MSG, AuthCode::MISSING_ACCESS_TOKEN);
case 'SymfonyComponentHttpKernelExceptionUnauthorizedHttpException':
case 'TymonJWTAuthExceptionsJWTException'://Token could not be parsed from the request.
case 'TymonJWTAuthExceptionsTokenBlacklistedException'://The token has been blackliste
return Results::failure(AuthCode::MISSING_ACCESS_TOKEN_MSG, AuthCode::MISSING_ACCESS_TOKEN);
case 'SymfonyComponentHttpKernelExceptionNotFoundHttpException':
return Results::failure(CommonCode::URL_NOT_FOUND_MSG, CommonCode::URL_NOT_FOUND);
case 'AppComponentsResultsExceptionServiceValidException':
case 'AppComponentsResultsExceptionServiceLogicException':
case 'AppComponentsResultsExceptionServiceErrorException':
case 'AppComponentsResultsExceptionServiceException':
return Results::failure($exception->getMessage(), $exception->getErrorCode());
case 'SymfonyComponentHttpKernelExceptionMethodNotAllowedHttpException':
return Results::failure($exception->getMessage(), $exception->getCode());
case 'DingoApiExceptionRateLimitExceededException':
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代码示例:
代码语言:javascript复制<?php
namespace AppComponentsResultsCode;
/**
* 公共的业务异常错误码
* Class CommonCode
* @package AppComponentsResultsCode
*/
class CommonCode
{
const INVALID_ARGS = "C_1";
const INVALID_ARGS_MSG = "参数无效";
const DATA_NOT_FOUND = "C_2";
const DATA_NOT_FOUND_MSG = "无数据";
//......
}
Results.php文件示例:
代码语言:javascript复制<?php
namespace AppComponentsResults;
use AppComponentsResultsCodeCommonCode;
use AppComponentsResultsCodeErrorCode;
use AppComponentsResultsCodeSuccessCode;
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);
}
代码语言:javascript复制Result.php文件示例
代码语言:javascript复制<?php
namespace AppComponentsResults;
use AppComponentsResultsCodeErrorCode;
use AppComponentsResultsCodeSuccessCode;
use AppComponentsResultsCodeErrorCode;
/**
* 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中注册新的异常处理
代码语言:javascript复制<?php
namespace AppProviders;
use IlluminateSupportServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
app('api.exception')->register(function (Exception $exception) {
$request = IlluminateHttpRequest::capture();
return app('AppExceptionsApiHandler')->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