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);
     }

 

posted @ 2022-01-23 17:33  wish_yang  阅读(279)  评论(0编辑  收藏  举报