Laravel Restful Api项目基础搭建
一:创建项目
composer create-project --prefer-dist laravel/laravel api
二:创建对应目录:利用命令创建文件参考:https://www.cnblogs.com/wish-yang/p/15834608.html
业务层:Services 封装业务逻辑
工具层:Supports 封装工具,类似短信,支付
契约层:Contracts 约束业务开发
门面:Facades 方便调用
三:Dingo来管理api
// 1、安装Dingo composer require dingo/api // 2、复制服务提供者文件 php artisan vendor:publish --provider="Dingo\Api\Provider\LaravelServiceProvider" // 3、.env配置 // 可参考:https://learnku.com/docs/dingo-api/2.0.0/Configuration/1444 API_SUBTYPE=ldj API_PREFIX=api API_VERSION=v1 API_DEBUG=true // 3、路由配置 $api = app('Dingo\Api\Routing\Router'); // 4、将bindings 注册路由中间 app/Http/Kernel.php protected $routeMiddleware = [ ... ... 'bindings' => \Dingo\Api\Transformer\Binding::class, ] $api->version('v1',[ 'middleware'=>['bindings'], 'namespace'=>"App\Http\Controller\V1", ],function($api){ });
四:JWT实现登录认证
// 1、安装jwt
composer require tymon/jwt-auth:dev-develop --prefer-source
// 2、复制服务提供者文件
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
// 3、设置jwt的secret,轻易别更换,会导致之前的token失效
php artisan jwt:secret
// 4、修改config/auth.php 将api的guard改为jwt
'api' => [
'driver' => 'jwt',
'provider' => 'users',//登录表
],
五:建UserController实现登录获取token
1、在user的model里实现JWTSubject接口的2个方法
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; use Tymon\JWTAuth\Contracts\JWTSubject; class User extends Authenticatable implements JWTSubject { use HasApiTokens, HasFactory, Notifiable; protected $fillable = [ 'name', 'mobile', 'password' ]; /** * 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 []; } }
2、添加路由,控制器,服务层 测试获取token
// 1、路由层 <?php $api = app('Dingo\Api\Routing\Router'); $api->version('v1',[ 'namespace' => 'App\Http\Controllers\Api\V1', ],function($api){ // 登录获取token $api->post('login', 'UserController@login'); // 根据token获取用户 $api->post('get-auth-user', 'UserController@getAuthUser'); }); // 2、控制器 public function login(Request $request) { $this->validate($request, [ 'mobile' => 'required', 'password'=> 'required', ]); // 这里还没做新增,密码需要加密 // dd(bcrypt($request->password)); $result = (new UserService())->login($request); return $result; } public function getAuthUser() { $this->validate($request, [ 'token' => 'required', ]); $result = (new UserService())->getAuthUser(); return $result; } // 3、服务层 public function login($request){ $input = $request->only(['mobile','password']); // 判断用户是否存在 // 判断token if (!$token = auth('api')->attempt($input)){ return [ 'error' => '用户密码错误' ]; } return ['token' => $token]; } public function getAuthUser(){ $user = JWTAuth::parseToken()->touser(); return $user; }
3、加入login.jwt中间件验证
// 1、将login.jwt 注册到路由中间件 app/Http/Kernel.php protected $routeMiddleware = [ .... 'login.jwt' => \App\Http\Middleware\LoginAuthVerify::class, ]; // 2、创建中间件 namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Tymon\JWTAuth\Exceptions\JWTException; use Tymon\JWTAuth\Exceptions\TokenExpiredException; use Tymon\JWTAuth\Facades\JWTAuth; use Tymon\JWTAuth\Http\Middleware\BaseMiddleware; class LoginAuthVerify extends BaseMiddleware { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle(Request $request, Closure $next) { //校验请求是否传递token $this->checkForToken($request); try{ //通过解析token进行身份认证 if ($user = JWTAuth::parseToken()->authenticate()){ return $next($request); } return response()->json([ 'code' => 400, 'error' => '未登录' ]); }catch (TokenExpiredException $exception){ try{ //检测到token过期,重新刷新一个token $token = $this->auth->refresh(); $access_token = 'Bearer'.$token; //将本次请求的token进行更换 $request->headers->set('Authorization',$access_token); //允许临时进行登录,一个过期的token只能有一次临时登录重新刷新token的机会 Auth::guard('api')->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']); }catch (JWTException $exception){ return response()->json([ 'code' => 400, 'error' => '您的身份认证已失效,请重新登录' ]); } } return $this->setAuthenticationHeader($next($request),$token); } } // 3、创建路由 $api->group(['middleware' => ['login.jwt']],function ($api){ $api->get('users/{id}', 'UserController@show'); }); // 4、创建控制器 public function show($id){ $result = (new UserService())->show($id); return $result; } // 5、创建服务层 public function show($id){ $user = User::find($id); return $user; } // 6、之后使用可以 (new UserService())->getAuthUser()->id;
六:创建服务提供者
// 1、创建服务提供者 php artisan make:provider LdjServiceProvider // 2、注册服务提供者 config/app.php 'providers' => [ ... ... App\Providers\LdjServiceProvider::class, ], // 3、编写服务提供者 public function register() { //使用singleton绑定单例 $this->app->singleton('user',function(){ return new UserService(); }); //使用bind绑定实例到接口以便依赖注入 // $this->app->bind('App\Contracts\TestContract',function(){ // return new TestService(); // }); } // 4、Controller调用 // 依赖注入 public function __construct(UserService $userService){ $this->userService = $userService; } // 更改方法 public function show($id){ // 这种方法也可以 // $userService = App('userService'); $result = $this->userService->show($id); return $result; }
七、异常处理
1、用户异常->不写日志
// 1、创建用户异常类 php artisan make:exception UserException // 2、打开app/Exceptions/UserException public function __construct($message='',$code=400) { parent::__construct($message,$code); } public function render() { return response()->json([ 'code' => $this->code, 'msg' => $this->message, 'data' => '' ],$this->code); } // 3、打开服务提供者处理下执行render方法 public function boot() { // 由于Dingo接管错误异常,没有执行render方法,这里处理下 app('api.exception')->register(function(\Exception $exception) { $request = Request::capture(); return app('App\Exceptions\Handler')->render($request,$exception); }); } // 4、控制器测试 $validate = Validator::make($request->all(), [ 'mobile' => 'required', 'password'=> 'required', ]); if($validate->fails()){ throw new UserException($validate->errors()->first()); } // 5、由于用户异常不写入日志,此处再app/Exceptions/Handler类添加 // 禁止写入日志 protected $dontReport = [ UserException::class, ];
2、系统异常->写日志
// 类似用户异常操作,区别在于需要记录日志和发送邮件 // 异常类处理 public function __construct($message='',$code=500) { parent::__construct($message,$code); } public function render() { // 记录错误日志 Log::error($this->message); // 发送邮件,钉钉 return response()->json([ 'code' => $this->code, 'msg' => '系统异常,联系管理员', 'data' => '' ],$this->code); }
八、标准结果处理
// 直接再app/controller/下的Controller基类里添加方法 /** * @param $data 返回值 * @param $code 返回码 * @param $message 返回说明 * @return Json */ public function success($data, $code = 200,$message = '成功') { return response()->json([ 'code' => $code, 'message' => $message, 'data' => $data, ],$code); }