Laravel5.5+passport 放弃 dingo 开发 API 实战,让 API 开发更省心

Laravel5.5更新,通过Laravel5.5开发Api更加顺畅了,在这里就分享一下Laravel开发Api的经验吧

1.封装返回的统一消息

返回的自定义消息,和错误消息,我自己封装了一个Trait,用来做基本的返回,Trait的封装如下

namespace App\Api\Helpers\Api;
use Symfony\Component\HttpFoundation\Response as FoundationResponse;
use Response;

trait ApiResponse
{
    /**
     * @var int
     */
    protected $statusCode = FoundationResponse::HTTP_OK;

    /**
     * @return mixed
     */
    public function getStatusCode()
    {
        return $this->statusCode;
    }

    /**
     * @param $statusCode
     * @return $this
     */
    public function setStatusCode($statusCode)
    {

        $this->statusCode = $statusCode;
        return $this;
    }

    /**
     * @param $data
     * @param array $header
     * @return mixed
     */
    public function respond($data, $header = [])
    {

        return Response::json($data,$this->getStatusCode(),$header);
    }

    /**
     * @param $status
     * @param array $data
     * @param null $code
     * @return mixed
     */
    public function status($status, array $data, $code = null){

        if ($code){
            $this->setStatusCode($code);
        }

        $status = [
            'status' => $status,
            'code' => $this->statusCode
        ];

        $data = array_merge($status,$data);
        return $this->respond($data);

    }

    /**
     * @param $message
     * @param int $code
     * @param string $status
     * @return mixed
     */
    public function failed($message, $code = FoundationResponse::HTTP_BAD_REQUEST, $status = 'error'){

        return $this->setStatusCode($code)->message($message,$status);
    }


    /**
     * @param $message
     * @param string $status
     * @return mixed
     */
    public function message($message, $status = "success"){

        return $this->status($status,[
            'message' => $message
        ]);
    }

    /**
     * @param string $message
     * @return mixed
     */
    public function internalError($message = "Internal Error!"){

        return $this->failed($message,FoundationResponse::HTTP_INTERNAL_SERVER_ERROR);
    }

    /**
     * @param string $message
     * @return mixed
     */
    public function created($message = "created")
    {
        return $this->setStatusCode(FoundationResponse::HTTP_CREATED)
            ->message($message);

    }

    /**
     * @param $data
     * @param string $status
     * @return mixed
     */
    public function success($data, $status = "success"){

        return $this->status($status,compact('data'));
    }

    /**
     * @param string $message
     * @return mixed
     */
    public function notFound($message = 'Not Found!')
    {
        return $this->failed($message,Foundationresponse::HTTP_NOT_FOUND);
    }

}

然后创建一个ApiController,通过所有的Api控制器继承该控制器,实现简洁的Api返回

<?php

namespace App\Http\Controllers\Api;

use App\Api\Helpers\Api\ApiResponse;
use App\Http\Controllers\Controller;


class ApiController extends Controller
{

    use ApiResponse;

    // 其他通用的Api帮助函数

}

然后,Api控制器就可以简洁的返回

<?php

namespace App\Http\Controllers\Api;

class IndexController extends ApiController
{
    public function index(){

        return $this->message('请求成功');
    }
}

2.资源类型的返回

资源返回通过5.5的新特性,API资源实现,具体参见
learnku.com/docs/laravel/5.5/eloqu...
使用方面比之前的Transformer方式更好用更优雅
比如返回用户的分页数据,只需要这样
文档已经很详细了,这里不再做概述

<?php

namespace App\Http\Controllers\Api;
use App\Models\User;
use App\Http\Resources\User as UserCollection;
use Illuminate\Support\Facades\Input;


class IndexController extends ApiController
{
    public function index(){

        return UserCollection::collection(User::paginate(Input::get('limit') ?: 20));

    }
}

3. Api授权模块

这里直接抛弃之前的jwt转向passport的认证方式,之前论坛已经有相关认证的帖子了
这里通过重写AuthenticatesUsers通过password的的授权模式模式进行实现

<?php
namespace App\Http\Controllers\Api;


use Carbon\Carbon;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Support\Facades\Auth;
use Laravel\Passport\Client;
use Socialite;

use App\Models\User;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Validator;


class AuthenticateController extends ApiController
{

    use AuthenticatesUsers;

    public function __construct()
    {
        $this->middleware('auth:api')->only([
            'logout'
        ]);
    }

    public function username()
    {
        return 'phone';
    }

    // 登录
    public function login(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'phone'    => 'required|exists:user',
            'password' => 'required|between:5,32',
        ]);

        if ($validator->fails()) {
            $request->request->add([
                'errors' => $validator->errors()->toArray(),
                'code' => 401,
            ]);
            return $this->sendFailedLoginResponse($request);
        }

        $credentials = $this->credentials($request);

        if ($this->guard('api')->attempt($credentials, $request->has('remember'))) {
            return $this->sendLoginResponse($request);
        }

        return $this->setStatusCode(401)->failed('登录失败');
    }

    // 退出登录
    public function logout(Request $request)
    {

        if (Auth::guard('api')->check()){

            Auth::guard('api')->user()->token()->revoke();

        }

        return $this->message('退出登录成功');

    }

    // 第三方登录
    public function redirectToProvider($driver) {

        if (!in_array($driver,['qq','wechat'])){

            throw new NotFoundHttpException;
        }

        return Socialite::driver($driver)->redirect();
    }


    // 第三方登录回调
    public function handleProviderCallback($driver) {

        $user = Socialite::driver($driver)->user();

        $openId = $user->id;


     // 第三方认证
        $db_user = User::where('xxx',$openId)->first();

        if (empty($db_user)){

            $db_user = User::forceCreate([
                'phone' => '',
                'xxUnionId' => $openId,
                'nickname' => $user->nickname,
                'head' => $user->avatar,
            ]);

        }

        // 直接创建token

        $token = $db_user->createToken($openId)->accessToken;

        return $this->success(compact('token'));

    }


    //调用认证接口获取授权码
    protected function authenticateClient(Request $request)
    {
        $credentials = $this->credentials($request);

            // 个人感觉通过.env配置太复杂,直接从数据库查更方便
        $password_client = Client::query()->where('password_client',1)->latest()->first();

        $request->request->add([
            'grant_type' => 'password',
            'client_id' => $password_client->id,
            'client_secret' => $password_client->secret,
            'username' => $credentials['phone'],
            'password' => $credentials['password'],
            'scope' => ''
        ]);

        $proxy = Request::create(
            'oauth/token',
            'POST'
        );

        $response = \Route::dispatch($proxy);

        return $response;
    }



    protected function authenticated(Request $request)
    {
        return $this->authenticateClient($request);
    }

    protected function sendLoginResponse(Request $request)
    {
        $this->clearLoginAttempts($request);

        return $this->authenticated($request);
    }

    protected function sendFailedLoginResponse(Request $request)
    {
        $msg = $request['errors'];
        $code = $request['code'];
        return $this->setStatusCode($code)->failed($msg);
    }
}

4.自定义返回异常

这里我的做法是直接拦截App\Exceptions\Handlerrender方法,实现自定义返回

<?php

namespace App\Exceptions;

use App\Api\Helpers\Api\ExceptionReport;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

class Handler extends ExceptionHandler
{
    /**
     *  其他代码...
     */

    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $exception
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $exception)
    {
        // 将方法拦截到自己的ExceptionReport
        $reporter = ExceptionReport::make($exception);

        if ($reporter->shouldReturn()){
            return $reporter->report();
        }

        return parent::render($request, $exception);
    }
}

然后在该方法实现自定义返回

<?php

namespace App\Api\Helpers\Api;


use Exception;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;

class ExceptionReport
{
    use ApiResponse;

    /**
     * @var Exception
     */
    public $exception;
    /**
     * @var Request
     */
    public $request;

    /**
     * @var
     */
    protected $report;


    /**
     * ExceptionReport constructor.
     * @param Request $request
     * @param Exception $exception
     */
    function __construct(Request $request, Exception $exception)
    {
        $this->request = $request;
        $this->exception = $exception;
    }


    /**
     * @var array
     */
    public $doReport = [
        AuthenticationException::class => ['未授权',401],
        ModelNotFoundException::class => ['该模型未找到',404]
    ];


    /**
     * @return bool
     */
    public function shouldReturn(){

        if (! ($this->request->wantsJson() || $this->request->ajax())){
            return false;
        }

        foreach (array_keys($this->doReport) as $report){

            if ($this->exception instanceof $report){

                $this->report = $report;
                return true;
            }
        }

        return false;

    }


    /**
     * @param Exception $e
     * @return static
     */
    public static function make(Exception $e){

        return new static(\request(),$e);
    }

    /**
     * @return mixed
     */
    public function report(){

        $message = $this->doReport[$this->report];

        return $this->failed($message[0],$message[1]);

    }


}

好啦,所有的基础模块都构建完了,现在就可以开发你需要的业务逻辑啦~

个人博客地址:www.timenotes.me/articles/code/11
欢迎大佬参观~

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由 Summer 于 6年前 加精
王举
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 169

赞。最近也在考虑放弃 Dingo,做了些定制后维护起来太麻烦了。
原生的一套兼容等各方面还是比较好。

6年前 评论
王举

@HyanCat 是呀,之前的项目就是用Dingo开发的,后台5.5的Api资源的确让人眼前一亮,新开的项目就直接用5.5重构了

6年前 评论

请问一下,passport使用密码模式登录,在登录验证的时候报这个错误是怎么回事啊:

BadMethodCallException
Method attempt does not exist.

控制器代码是这样的:

if (!Auth::guard('api')->attempt(['account' => $account, 'password' => $password])) {
    return response(['message' => '用户名或者密码错误'], 403);
}

auth.php配置是这样的:

'api' => [
        'driver' => 'passport',
        'provider' => 'users',
 ],
'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
 ],

模型是这样的:

namespace App\Models;

use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
6年前 评论
王举

@justinstar Auth::guard('api')拿到的是RequestGuard对象,没有attempt方法呀,试试Auth::attempt(xxx)

6年前 评论

@王举 这样怎么来指定auth.php里面的api呢,

'defaults' => [
    'guard' => 'web',
    'passwords' => 'users',
],
'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'admin_users',
    ],

    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],
6年前 评论
王举

@justinstar 做法是use AuthenticatesUsers;使用这个Trait,然后通过$this->guard('api')->attempt([])的方式,这个会获取SessionGuard的实例,调用里面的attempt方法,passport中的RequestGuard默认没有实现attempt方法,或者你重写一个Guard都可以

6年前 评论

@王举 好了,非常感谢:smile:

6年前 评论

想问一下,@王举 api 参数校验这一块,你是如何封装并的呢?laravel提供的校验类好像都是针对form表单提交过来的数据校验。

6年前 评论
王举

@Thank 前面已经提过可以拦截请求过程的Exception,(4.自定义返回异常)[https://learnku.com/articles/6035/laravel55-developing-api-combat#4自定义返回异常],其实表单验证也是可以拦截的呀
比如,在Api控制器我们用表单验证

public function index(Request $request){

        $this->validate($request,[

            'test1' => 'required',
            'test2' => 'min:5'

        ]);
    }

然后在我们的拦截器里面拦截Illuminate\Validation\ValidationException异常

/**
     * @var array
     */
    public $doReport = [
        AuthenticationException::class => ['未授权',401],
        ModelNotFoundException::class => ['改模型未找到',404],
        ValidationException::class => []
    ];

然后就可以直接拦截到之后就可以自定义返回了

/**
     * @return mixed
     */
    public function report(){

        if ($this->exception instanceof ValidationException){

            return $this->failed($this->exception->errors());

        }
        $message = $this->doReport[$this->report];

        return $this->failed($message[0],$message[1]);

    }

效果:
file

6年前 评论
赖皮小鳄鱼 3年前

@王举 好,我先按你的思路进行测试。谢谢。

6年前 评论

ApiResponse 里的failed 方法有问题,调用这个方法如果传递 status code 参数的话,status 这个方法就会用400 覆盖掉 setStatusCode 设置的状态吗!

比如 notFound 方法 返回的 status code 是400

另外个人觉得 status 这个方法有悖于 RESTfull API 的设计规范,进行无意义的包装……我之前也是这么做的,但是用 axios 调用接口时,要 response.data.data,这样比较郁闷!

6年前 评论
赖皮小鳄鱼 3年前

$user->createToken();这个方法创建的是Personal Token 有效期一年,有没有什么方法创建 普通的 AccessToken

6年前 评论
王举

@Jinrenjie
多谢指点,已更新。
我觉得status的包装是有必要的,在js你可能觉得这样麻烦,但是api不仅仅用在web,IOS安卓都是要使用的。特别是移动端要根据状态去判断请求的结果。或者拿JSON转换成相应的对象去展示。这样里面包装的data就很有必要了。而且response.data.data的两个data又不冲突,第二个data是可能有可能没的,要根据请求状态判断
Personal Token的有效期可以设置的,具体可以参考这个 https://github.com/laravel/passport/issues...
感觉这样的话应该能满足你的需求

6年前 评论

@王举 移动端我没接触过,难道不能根据 HTTP 状态码判断吗?
还有个问题,就是关于 POST、PUT和 PATCH 者三种操作如果成功的话都是返回 201状态码吗?有人说是 PUT和 PATCH 是200,也有人说是201 但是规范上好像说201 是创建新资源对象才返回的状态码!

感谢分享,从你的这篇文章里学到了不少东西:smile: :smile: :smile:

6年前 评论
franktrue

请教个问题,在passport密码授权模式下使用手机号密码登陆获取access_token。当接入APP端第三方登陆。这时候如何生成access_token呢?
因为使用第三方授权登陆的时候是没有手机号password等信息的。access_token好像只能请求/oauth/token获取,没有像jwt那样获取$user,然后调用fromUser($user)就可以生成token.

6年前 评论

@fnf1993 这个只能创建 Personal Token,根据手机号码获取用户模型,$user->createToken('name_string');

6年前 评论
王举

@fnf1993 第三方授权的话我选用这个
然后这样就可以创建Token了

$token = $db_user->createToken($openId)->accessToken;

这种个人令牌默认有效期是一年
你可以进行修改,参考:https://github.com/laravel/passport/issues...
使用文档地址:
https://learnku.com/docs/laravel/5.5/passport#管理个人访问令牌

6年前 评论
franktrue

@Jinrenjie thx
我去试试~

6年前 评论
franktrue

@王举 thx
我去看看,非常感谢

6年前 评论
franktrue

@王举 还有个问题
把第三方登陆的标识信息(openid微信、uid微博等存在下图name的位置),那如何区分不同平台呢?
file

我现在的想法是加个前缀eg.

  1. 微信
$db_user->createToken('wechat'.$openId)->accessToken;

2.

6年前 评论
franktrue

@王举

2.微博

$db_user->createToken('weibo'.$uid)->accessToken;

这样可以嘛?

6年前 评论
王举

@fnf1993 可以的,创建token的名字随意

6年前 评论
medz

用了半年的 dingo,控制器,依赖注入都被废了。最后放弃了。

6年前 评论

@medz 歇了好久没看,到底 passport 和 dingo/api 有啥区别啊,我还是5.2的时候用过dingo

6年前 评论
medz

@kylesean 你理解错 Passport 和 dingo/api 了,Passport 是 Laravel 官方提供的 oAuth 2.0 实现包。dingo/api 是一个标准 REST ful API 开发拓展框架。

6年前 评论

@medz 哦哦,3Q,可是 dingo/api 不也封装了 oauth 吗? passport 更有优势么?

6年前 评论
medz

@kylesean passport 只是单纯封装 oauth。

6年前 评论
class Controller extends BaseController {
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

    //json格式输出
    public function outPutJson($data, $code = 200, $message = NULL) {
        $message = $message ?? config('code')[$code];
        return json_encode(['code' => $code, 'message' => $message, 'data' => $data], JSON_UNESCAPED_UNICODE);
    }
}

我直接这样的,在config/code.php里面写所有的错误码和对应的信息。然后调用

return $this->outPutJson($merge_data);
6年前 评论
王举

@欧阳逸 也是可以的,这个自己感觉怎么省事怎么来

6年前 评论

@王举 大哥,我按照你的方法来,请求登录接口的时候

(1/1) BadMethodCallException

Method attempt does not exist.
引用的和你一样的,只是我继承的是Controller,我的Controller代码如下:


<?php

namespace App\Http\Controllers;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;

class Controller extends BaseController {
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

//json格式输出
public function outPutJson($data, $code = 200, $message = NULL) {
    $message = $message ?? config('code')[$code];
    return json_encode(['code' => $code, 'message' => $message, 'data' => $data], JSON_UNESCAPED_UNICODE);
}

}

我的D:\phpStudy\WWW\api.douxiaoli.com\app\Http\Controllers\AuthenticateController.php登录代码如下
// 登录
public function login(Request $request) {
    $validator = Validator::make($request->all(), [
        'name' => 'required|string|max:255',
        'password' => 'required|string|min:6',
    ]);

    if ($validator->fails()) {
        return $this->outPutJson('', 402, $validator->errors()->all());
    }

    $credentials = $this->credentials($request);

    if ($this->guard('api')->attempt($credentials, $request->has('remember'))) {
        return $this->sendLoginResponse($request);
    }

    return $this->outPutJson('', 401);
}

请问为什么会这样呢?
6年前 评论

@王举 login的时候$this->guard('api')->attempt($credentials, $request->has('remember')),我这个拿到的也是RequestGard,我没看到你在AuthenticatesUsers这里面写attempt方法啊,我在这里卡住了,能够指导下吗?多谢了

6年前 评论

@王举 试过Auth::attempt(),也不行啊。

6年前 评论

@欧阳逸 如果你用的是tymon/jwt-auth的包的话,升级到1.0.0-rc.1还要在User 模型里重写一个方法,具体的我也忘记了,你可以参照源码,因为这些在官方文档里都没有得到更新……

6年前 评论

@Jinrenjie 我直接用的官方自带的passport,没用第三方包

6年前 评论

@欧阳逸 Passport 没有 实现Guard的类,他的$this->guard('api')不是passport 的验证方式……

6年前 评论

@王举 移动端android ios什么的用http状态码一点问题都没有,实践过多次,如果不是为了RPC接口没必要,而且更方便。 一直没有包装http code在json里,完全没必要!最多就是有一些业务细节的code代码

6年前 评论
王举

@ThinkWorld 其实不用也是完全可以的,这个是参考其他api的返回,比如dingo的错误也会返回如下

{
    "message": "User was updated prior to your request.",
    "status_code": 409
}

说不上用不用的好坏,主要是和前端人员沟通好才是最重要的,ios是可以判断,但是打印的json结果如果不带code的话默认是看不到状态码的,要单独在response中拿code,个人感觉还是带了方便一些。

6年前 评论

@王举 那就有备无患吧。

6年前 评论

有个bug,当我使用$this->success(new UserCollection(User::paginate()))返回分页资源时,返回的json数据中没有 meta 和 links 键包含的分页状态信息

6年前 评论
王举

@ganlanshu0211 resources资源直接返回

return new UserCollection(User::find(1));

或者自定义

return $this->success([
 'name' => 'zhangsan',
 'say' => 'hello'
]);
6年前 评论

@王举 这样不就不能再json数据里面返回status了,想全部做成一样的格式的

6年前 评论
王举

@ganlanshu0211 给你个思路,就是重写路由的Api Response 返回
目前没有发现很简单的方法,思路是重写Json\Resourcewarp方法
直接贴代码了,你自己研究一下

<?php

namespace App\Http\Responses;

use Illuminate\Http\Resources\Json\Resource as BaseResource;

class Resource extends BaseResource
{

    public static function collection($resource)
    {

        $resource = new class($resource, get_called_class()) extends ResourceCollection {
            /**
             * @var string
             */
            public $collects;

            /**
             * Create a new anonymous resource collection.
             *
             * @param  mixed  $resource
             * @param  string  $collects
             * @return void
             */
            public function __construct($resource, $collects)
            {
                $this->collects = $collects;

                parent::__construct($resource);
            }
        };

        return $resource;

    }

    public function toResponse($request)
    {
        return (new ResourceResponse($this))->toResponse($request);
    }

}

App\Http\Responses\ResourceCollection

<?php

namespace App\Http\Responses;

use IteratorAggregate;
use Illuminate\Pagination\AbstractPaginator;
use Illuminate\Http\Resources\CollectsResources;

class ResourceCollection extends Resource implements IteratorAggregate
{
    use CollectsResources;

    /**
     * The resource that this resource collects.
     *
     * @var string
     */
    public $collects;

    /**
     * The mapped collection instance.
     *
     * @var \Illuminate\Support\Collection
     */
    public $collection;

    /**
     * Create a new resource instance.
     *
     * @param  mixed  $resource
     * @return void
     */
    public function __construct($resource)
    {

        parent::__construct($resource);

        $this->resource = $this->collectResource($resource);
    }

    /**
     * Transform the resource into a JSON array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return $this->collection->map->toArray($request)->all();
    }

    /**
     * Create an HTTP response that represents the object.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function toResponse($request)
    {
        return $this->resource instanceof AbstractPaginator
            ? (new PaginatedResourceResponse($this))->toResponse($request)
            : parent::toResponse($request);
    }

}

App\Http\Responses\PaginatedResourceResponse

<?php

namespace App\Http\Responses;
use Illuminate\Http\Resources\Json\PaginatedResourceResponse as BasePaginatedResourceResponse;

class PaginatedResourceResponse extends BasePaginatedResourceResponse
{

    protected function wrap($data, $with = [], $additional = [])
    {

        $meta = [
            "status"=> "success",
            "code" => $this->calculateStatus(),
        ];
        $parent = parent::wrap($data, $with, $additional);
        return array_merge_recursive($meta,$parent);
    }
}

App\Http\Responses\ResourceResponse

<?php
namespace App\Http\Responses;
use Illuminate\Http\Resources\Json\ResourceResponse as BaseResourceResponse;

class ResourceResponse extends BaseResourceResponse
{
    protected function wrap($data, $with = [], $additional = [])
    {
        $meta = [
            "status"=> "success",
            "code" => $this->calculateStatus(),
        ];
        $parent = parent::wrap($data, $with, $additional);
        return array_merge_recursive($meta,$parent);

    }

}

然后所有的Resource都继承自己的App\Http\Responses\Resource
当然,meta是我随便定义的,你可以自由定义
example

<?php

namespace App\Http\Resources;

//use Illuminate\Http\Resources\Json\Resource;

use App\Http\Responses\Resource;

class User extends Resource
{

    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request
     * @return array
     */
    public function toArray($request)
    {

        return [
            'nickname' => $this->nickname,
            'id' => $this->id,
            'head' => $this->head,
            'signature' => $this->signature,

        ];

    }
}

然后所有的资源既可以带你加的meta了

6年前 评论

ios工程师初学laravel,特别感谢!

6年前 评论

用了资源类型的返回,就不知道封装的这个Trait,怎么使用了.

6年前 评论

@Ali 是不是错误的返回类型,可以使用封装的这个

6年前 评论
王举

@Ali 错误的消息返回,以及特殊的返回格式,用户提示等

6年前 评论

@Thank 问您一下,你实验成功了吗.我一直不成功,不走验证这一步.

6年前 评论
王举

@Ali 嗯,可能你代码还有问题吧

6年前 评论

@王举 也可能是我不知道 怎么调用封装的这些方法吧.

6年前 评论

为什么这个 第三方回调方法里你 return 了东西。。。。你返回给谁了。懵逼

6年前 评论
王举

@geekzwb 建议你看看这个,就懂啦
http://www.ruanyifeng.com/blog/2014/05/oau...

6年前 评论

@王举 我的理解是,这个是第三方比如qq重定向过来的吧。如果你在这个回调方法return success方法, 浏览器页面显示是你返回的 json?

6年前 评论
王举

@geekzwb 授权成功会会掉到这里的,这些你都可以参考第三方授权的接口文档,说的很清楚

6年前 评论

@王举 我不明白的是,这个返回是发送到 前端的? 对应前端哪个请求? 可是这个回调并不是前端发起的呀,而是qq

6年前 评论
王举

@geekzwb 前端请求你的地址 -> 你跳转到第三方授权的地址 -> 授权成功回调你设置的回调地址 -> 处理登录信息

6年前 评论
王举

@geekzwb http://wiki.connect.qq.com/%E7%BD%91%E7%AB... 你可以参考qq的第三方登录文档

6年前 评论

@王举 前端请求服务端地址,这里的请求是指的跳转还是ajax请求?

6年前 评论
王举

@geekzwb 跳转

6年前 评论

我创建了一个login的表单请求,

6年前 评论

我创建了一个login的表单请求后,就突然开始提示 unsupported_grant_type,感觉八竿子打不着的俩东西,有点懵逼,能帮我解下惑吗?

6年前 评论

@Ali 成功了,但没整理成文档。

6年前 评论

我想在lumen使用jwt 按照文档进行总是各种报错
这是其中的一处错误
lumen版本5.5 jwt0.5
(1/1) ErrorException
Non-static method Tymon\JWTAuth\JWTAuth::attempt() should not be called statically
有没有相关的安装文档 非常感谢

6年前 评论

@王举 $status = "success" 返回参数中的"status"起到什么作用?,成功失败不是根据code来的吗?code=200表示成功?

6年前 评论
王举

@torome 这个用来表示用户的请求状态是否成功,觉得多余可以直接去掉的,没啥关系

6年前 评论

大佬 我想问下 如果是单页面应用 在passport密码授权模式下 获取到token 保存在哪里 ?如果别人获取到这个token 是不是对用户的安全方面有一些问题

6年前 评论
王举

@墨韵留香 spa的话,直接将token用js保存到localStorge就好了,在 App.vue created的时候拿到它,判断用户的登录状态,如果用户特意拿的话可能会请求到你的相应的接口,如果考虑token安全问题的话,可以考虑token的失效周期或者用户的地理位置检测方面考虑

6年前 评论

@王举 嗯嗯 好的 谢谢 我试试用ip 进行判断下

6年前 评论

@ganlanshu0211
@王举
关于分页的我想了个办法解决,在ApiResponse中增加一个方法来组合数据

/**
     * 含有分页的资源返回方法
     *
     * @param AnonymousResourceCollection $collection
     * @param string $status
     * @param null $code
     * @return mixed
     */
    public function respondForPaginate(AnonymousResourceCollection $collection, $status = 'success', $code = null)
    {
        if ($code) {
            $this->setStatusCode($code);
        }

        $status = [
            'status' => $status,
            'code' => $this->statusCode
        ];

        return $collection->additional($status);
    }
6年前 评论

我很无解释~还是没有弄懂怎么设置个人令牌的时间
已经新建了ServiceProvider.php
之后不知道怎么应用....

6年前 评论

按你的代都贴去了,对于我这个新手来说,实在不知如何入手啊,能不能给个完整的测试实例啊,

6年前 评论
'guards' => [
    'admin'=>[
        'driver'=>'session',
        'provider'=>'admin_users',
    ],
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'passport',// 'driver' => 'token',
        'provider' => 'api_users',// 'provider' => 'users',
    ],
], 

'providers' => [
'admin_users'=>[
'driver'=>'eloquent',
'model' =>App\Models\AdminUser::class,
],
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'api_users' => [
'driver' => 'eloquent',
'model' => App\Models\ApiUser::class,
],

    // 'users' => [
    //     'driver' => 'database',
    //     'table' => 'users',
    // ],
],
6年前 评论

//if ($this->guard('api')->attempt($credentials,$request->has('remember'))) {
// return $this->sendLoginResponse($request);
//}
这个代码 总是出错

6年前 评论

原来只支持默认 User模型!!!!!!

6年前 评论

大神 想请教一下 ,在你的第三方登录的两个方法中 redirectToProviderhandleProviderCallback 这两个方法的路由是写在 api.php 中 还是 web.php 中呢?

6年前 评论
王举

@dptms 属于web的

6年前 评论

@王举 那如果让 laravel 纯粹写 api 第三方登录有什么好的实践办法吗?

6年前 评论
王举

@dptms 文档是最好的实践方法啊

6年前 评论

看了你的帖子,心里很纠结究竟要不要用dingo,个人也觉得dingo提供的一些我用到的功能,实现方式并不复杂,用上dingo反而显得累赘,担心后期维护起来会很麻烦.顺便提个问题,如果不使用dingo,如何实现api接口的版本控制呢?

6年前 评论
王举

@山海王子 个人建议彻底抛弃dingo,后端项目复杂的时候真的用的难受,抛弃Fractal的Transformer,Api资源无论灵活性还是封装都是完全超过Fractal的,以后用的难受了再迁移,为啥不提前直接用新特性呢,dingo自带了版本控制,直接看dingo的文档就知道啦

6年前 评论

重要的事要大一点

老兄,你能在写全一点吗?毕竟有些人接触API比较少。看不大懂?

6年前 评论

请问, 如果根据客户端传过来的参数, 动态查找关联模型的数据, 用 Api 资源要怎么实现 ?

6年前 评论

研究了一下, 知道怎么玩了~ :)

6年前 评论

@王举

file

手机号和密码是对的 出现图片的那写 这是什么问题啊 帮帮忙 :disappointed_relieved:

6年前 评论
王举

@木拉地力 缺少username 字段

6年前 评论
王举

@木拉地力 重写AuthenticatesUsers的trait的username 方法为你自己的字段,用passport的话,在user模型实现findForPassport方法

6年前 评论

@王举

file

username字段是我用tel字段为用户名的所有使用用户名的地方我都改为tel了

还是一样

6年前 评论
王举

@木拉地力 你的错误是请求非法,缺少了一个必选参数,你按照报错好好找一下,自己debug很容易找到的

6年前 评论

@王举

file

我刚才是把上面的username改为tel了所以报刚才那个错误
这个username的值是传过来的密码 可是我数据库字段里的字段是phone(我把tel该phone了)这样对吗
现在改回来了报下面这个图的错误

file

6年前 评论
王举

@木拉地力 左边的username的key不能改,修改的不应该是你右边的$credentials['xxx']么,你传的username是tel的话应该就是$credentials['tel'],和左边对应起来

6年前 评论

@王举 现在对应起来了报这样的错误
{
"error": "invalid_credentials",
"message": "The user credentials were incorrect."
}

6年前 评论
逍遥客

@木拉地力 上面说过要自己实现 『在 User 模型实现 findForPassport 方法』

对应你的 User 模型应该是这样的,你试试吧。

    public function findForPassport($username)
    {
        return self::where(’tel', $username)->first();
    }
6年前 评论

异常无法处理 ,跳去登录页了

6年前 评论

@Kerlin 非常感谢你提供的分页思路,真的非常感谢

5年前 评论

@王举 个人觉得 封装的这个ExceptionReport 意思不大呢

在封装用中 $this->exception typeof 异常类型 和 在本身的Handler 中判断是一样的

另外更想知道api resource 用Response::json 返回会丢失 meta,links数据等数据而直接return 就不会的原因,不知你有没有见解

5年前 评论

根据这篇文章,自定义了一个HelperFunction

<?php

use Symfony\Component\HttpFoundation\Response;
use Illuminate\Pagination\LengthAwarePaginator;

if (! function_exists('stored')) {
    /**
     * 创建资源成功后响应
     *
     * Date: 21/03/2018
     * @author George
     * @param $data
     * @param string $message
     * @return \Illuminate\Http\JsonResponse
     */
    function stored($data, $message = '创建成功') {
        return respond($data, $message);
    }
}

if (! function_exists('updated')) {
    /**
     * 更新资源成功后响应
     *
     * Date: 21/03/2018
     * @author George
     * @param $data
     * @param string $message
     * @return \Illuminate\Http\JsonResponse
     */
    function updated($data, $message = '更新成功') {
        return respond($data, $message);
    }
}

if (! function_exists('deleted')) {
    /**
     * 删除资源成功后响应
     *
     * Date: 21/03/2018
     * @author George
     * @param string $message
     * @return \Illuminate\Http\JsonResponse
     */
    function deleted($message = '删除成功') {
        return message($message, Response::HTTP_OK);
    }
}

if (! function_exists('accepted')) {
    /**
     * 请求已被放入任务队列响应
     *
     * Date: 21/03/2018
     * @author George
     * @param string $message
     * @return \Illuminate\Http\JsonResponse
     */
    function accepted($message = '请求已接受,等待处理') {
        return message($message, Response::HTTP_ACCEPTED);
    }
}

if (! function_exists('notFound')) {
    /**
     * 未找到资源响应
     *
     * Date: 21/03/2018
     * @author George
     * @param string $message
     * @return \Illuminate\Http\JsonResponse
     */
    function notFound($message = '您访问的资源不存在') {
        return message($message, Response::HTTP_NOT_FOUND);
    }
}

if (! function_exists('internalError')) {
    /**
     * 服务器端位置错误响应
     *
     * Date: 21/03/2018
     * @author George
     * @param $message
     * @param int $code
     * @return \Illuminate\Http\JsonResponse
     */
    function internalError($message = '未知错误导致请求失败', $code = Response::HTTP_INTERNAL_SERVER_ERROR) {
        return message($message, $code);
    }
}

if (! function_exists('failed')) {
    /**
     * 错误的请求响应
     *
     * Date: 21/03/2018
     * @author George
     * @param $message
     * @param int $code
     * @return \Illuminate\Http\JsonResponse
     */
    function failed($message, $code = Response::HTTP_BAD_REQUEST) {
        return message($message, $code);
    }
}

if (! function_exists('success')) {
    /**
     * 成功响应
     *
     * Date: 21/03/2018
     * @author George
     * @param $date
     * @return \Illuminate\Http\JsonResponse
     */
    function success($date) {
        return respond($date);
    }
}

if (! function_exists('message')) {
    /**
     * 消息响应
     *
     * Date: 21/03/2018
     * @author George
     * @param $message
     * @param int $code
     * @return \Illuminate\Http\JsonResponse
     */
    function message($message, $code = Response::HTTP_OK) {
        return respond([], $message, $code);
    }
}

if (! function_exists('respond')) {
    /**
     * 生成响应体
     *
     * Date: 21/03/2018
     * @author George
     * @param array $data
     * @param string $message
     * @param int $code
     * @param array $header
     * @return \Illuminate\Http\JsonResponse
     */
    function respond($data = [], $message = '请求成功', $code = Response::HTTP_OK, array $header = []) {
        if ($data instanceof LengthAwarePaginator) {
            return response()->json([
                'resultCode' => $code,
                'resultMessage' => $message,
                'data' => $data->items(),
                'current_page' => $data->currentPage(),
                'from' => $data->firstItem(),
                'per_page' => $data->perPage(),
                'to' => $data->lastItem(),
                'total' => $data->total(),
            ], $code, $header);
        }
        return response()->json([
            'resultCode' => $code,
            'resultMessage' => $message,
            'data' => $data ? $data : []
        ], $code, $header);
    }
}

在控制器里:

public function index()
{
    $companies = Company::query()->paginate(15);
    return success($companies);
}
5年前 评论
984054610

请问这个能不能是实现,手机号码+短信验证码登录认证,可以的话,大概思路是什么?

5年前 评论
王举

@984054610 这个具体的功能要你自己写,大致就是 根据手机号 发送验证码,然后将手机号和发送的验证码保存到缓存,并设置缓存周期以及重试次数,在注册的时候拿注册的手机号和验证码进行比对,成功则创建(注册)用户。

这个是一年前写的注册;逻辑,你可以参考一下
https://github.com/wowiwj/LifeRecoder-Serv...

5年前 评论

@王举 我在用这个方法在本地Homestead可以顺利获取到access_token,获取用户也没有问题,部署到服务器上之后,也可以顺利的获取到access_token,然后使用access_token获取用户之后,提示

'message' : 'Unauthorized'

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return \Auth::guard('api')->user();
});

本地这个方法可以获取到用户,但是部署到服务器上就不可以了,发现这个auth:api没有起作用,整了一个下午,请指教,本地是Homestead,服务器端是Centos+Apache

5年前 评论

@王举 请问如果跨域怎么处理,我现在是在和你类似的响应封装里添加的头部

 return response()->json($data, $this->getStatusCode())
            ->header('Access-Control-Allow-Origin', '*')// $origin
            ->header('Access-Control-Allow-Headers', 'Origin, Content-Type, Cookie, X-CSRF-TOKEN, Accept, Authorization, X-XSRF-TOKEN,X-Requested-With')
            ->header('Access-Control-Expose-Headers', 'Authorization, authenticated')
            ->header('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, OPTIONS,DELETE')
            ->header('Access-Control-Allow-Credentials', 'true'); // 允许浏览器发送 cookie 至服务器

外加 barryvdh/laravel-cors 这个包(不用没法跨域)
单纯用这个包,系统在抛异常时就没法跨域 :smile:
想请教你有没有好的跨域处理方案,网上好多都是用 middleware,但是异常时不走 middleware :smile:

5年前 评论
王举

@Johnson16 有木有执行passport:install? 部署没问题应该就是这个问题了

5年前 评论

@王举 非常感谢回答,找到原因了,是因为服务器上的Apache挂了,卸载重新安装了Nginx后,一切顺溜。:pray:

5年前 评论
王举

@江渚之上 系统的异常可以拦截和自己定制的,在文章自定义异常的位置你可以加api跨域设置,不过线上项目正常是不允许系统异常的,有异常可以自己测试修改呀 :stuck_out_tongue_closed_eyes:

5年前 评论

@王举 access_token 过期,会抛异常的,这个时候应用就不可以跨域了,在自定义异常处理中拼接跨域响应头 这样真的饿好吗 查了很久 没见过呀 :smile:

5年前 评论
王举

@江渚之上 我用的5.5,加了一个middleware,没有遇到你这种情况
middleware

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Response;

class EnableCrossRequestMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        $origin = $request->server('HTTP_ORIGIN') ? $request->server('HTTP_ORIGIN') : '';
        $allow_origin = config('manager.sites');

        if (in_array($origin, $allow_origin)) {

            $response->header('Access-Control-Allow-Origin', $origin);
            $response->header('Access-Control-Allow-Headers', 'Origin, Content-Type, Cookie, X-CSRF-TOKEN, Accept, Authorization, X-XSRF-TOKEN');
            $response->header('Access-Control-Expose-Headers', 'Authorization, authenticated');
            $response->header('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, OPTIONS');
            $response->header('Access-Control-Allow-Credentials', 'true');
        }
        return $response;
    }
}

kernel

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\TrustProxies::class,
        \App\Http\Middleware\EnableCrossRequestMiddleware::class
    ];

    /**
     * The application's route middleware groups.
     *
     * @var array
     */
    protected $middlewareGroups = [
        'web' => [
            ...
        ],
       ...
5年前 评论

@王举 我想用我自己新建的用户表,attempt方法这里一直报错,attempt方法只支持默认User模型么 :joy:

5年前 评论

@Johnson16 请问你用的是laravel自带的User模型么,我用自己新建的模型表,一到$this->guard('api')->attempt($credentials,$request->has('remember'))就报错,求助

5年前 评论

@xuhui

自定义用户表的模型文件要这样写:

<?php

namespace App;

use Laravel\Passport\HasApiTokens;
use Illuminate\Foundation\Auth\User as Authenticatable;

class OtherUser extends Authenticatable  //这里的OtherUser就是其他用户表
{
    use HasApiTokens;  //这一行不能缺
}

多用户表登录参考这篇文章:
教程: Laravel 5.3 多用户表登录

然后看app/auth.php文件

 'guards' => [
        'api' => [
            'driver' => 'passport',
            'provider' => 'users',
        ],

        'other_users_api' => [  //这里注意
            'driver' => 'passport',
            'provider' => 'other_users',  //这里注意,就是下方的other_users
        ],
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => \App\Models\User::class
        ],

        'other_users' => [
            'driver' => 'eloquent',
            'model' => \App\Models\OtherUser::class  //这里是你其他用户表的模型class
        ],
    ],

然后验证的时候

$this->guard('other_users_api')->attempt($credentials,$request->has('remember'))
5年前 评论

@Johnson16
这张数据表不是通过migrate创建的,是直接同事从之前的项目拉过来的,我感觉这是唯一的区别吧,别的地方我基本都是照做的呀 :joy:
file
file

5年前 评论

@xuhui 你的Customer模型文件确定是绑定user表吗?你的具体表名是什么?有没有系统自带的users表?

5年前 评论

@Johnson16 是不是必须按照多用户的形式搞下面就可以行的通,我现在准备按照你的重新弄一便

5年前 评论

@xuhui 必须按照多用户表的方式去做,应为你这样操作,原生的users表的验证就会被替换

5年前 评论

@Johnson16 谢谢指导,我现在重新创建一个项目尝试一下

5年前 评论

@Johnson16 大神,多用户表登录搞出来了,也可以正常登录,但是为什么$this->guard('api')->attempt($credentials, $request->has('remember'))调用的还是原本User模型来验证,

file

file

原本的表数据完全登录不上去,而且不能使用Auth::guard('api')去调用attempt方法,不然会报错方法不存在 :joy: :joy:

file

5年前 评论

目前在做一个前后端分离的网站,用laravel只做接口,现在不太明白的是,前台用户登录坑定要用账号密码获取token值以便访问登陆后的其他页面,那么没登录的时候,对接口的访问是否也是要带上token的,这个token怎么给予客户端?

5年前 评论
王举

@Alexanderwmc 没登录默认没有token啊,需要授权的接口肯定要禁止访问的呀

5年前 评论

请问,中间件验证登录失败后返回的是web登录页面?如何设定返回api提示登录
自问自答一下,
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
],

5年前 评论

@王举

file

这么写有问题啊。 我一直报我上面的错误 我换成文档的那种写法就好了

file

5年前 评论

@王举 哥你这个自定义异常好像也有点问题。token过期后并没有拦截 我这边直接去了我后台的登陆页

5年前 评论

file

这里是把参数验证放在控制器里,如果参数多了,放在控制器就不合适。
新建一个 LoginRequest 也不能返回想要的 json。
请问大家有好的解决方法吗。

5年前 评论
王举

@王成涛 可以获取的,在app/Exceptions/Handler 的 render 方法中拦截 ValidationException 即可, 获取你可以处理根据之前的
4.自定义返回异常相关代码

这个是我之前的实现

<?php

namespace App\Api\Helpers\Api;

use Exception;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;

class ExceptionReport
{
    use ApiResponse;

    /**
     * @var Exception
     */
    public $exception;
    /**
     * @var Request
     */
    public $request;

    /**
     * @var
     */
    protected $report;

    /**
     * ExceptionReport constructor.
     * @param Request $request
     * @param Exception $exception
     */
    function __construct(Request $request, Exception $exception)
    {
        $this->request = $request;
        $this->exception = $exception;
    }

    /**
     * @var array
     */
    public $doReport = [
        AuthenticationException::class => ['未授权',401],
        ModelNotFoundException::class => ['改模型未找到',404],
        AuthorizationException::class => ['没有此权限',403],
        ValidationException::class => [],

    ];

    public function register($className,callable $callback){

        $this->doReport[$className] = $callback;
    }

    /**
     * @return bool
     */
    public function shouldReturn(){

        if (! ($this->request->wantsJson() || $this->request->ajax())){

            return false;
        }

        foreach (array_keys($this->doReport) as $report){

            if ($this->exception instanceof $report){

                $this->report = $report;
                return true;
            }
        }

        return false;

    }

    /**
     * @param Exception $e
     * @return static
     */
    public static function make(Exception $e){

        return new static(\request(),$e);
    }

    /**
     * @return mixed
     */
    public function report(){

        if ($this->exception instanceof ValidationException){

            $error = array_first($this->exception->errors());

            return $this->failed(array_first($error),$this->exception->status);

        }

        $message = $this->doReport[$this->report];

        return $this->failed($message[0],$message[1]);

    }

}
5年前 评论

感觉没有dingo➕jwt简单啊

5年前 评论
王举

@等车的猪 dingo 感觉体验不是很好,不过jwt确实用着比passport要简单的多,所以项目选用我一般优先选用jwt,只是jwt-auth的文档还是0.5的,1.0目前还都是release版本。

5年前 评论
domine

上述的方法中如果未授权返回的结果为什么是如下图,而不是自定义的异常格式

file

5年前 评论

@王举

我比较纠结一个问题!那个guard方法不接受参数的啊,$this->guard('api') 和$this->guard() 的返回都是sessionGuard啊加个‘api’没意义啊!这样做相当于还是在用 ‘web’这个默认守卫做验证,如果web守卫和api守卫的表名不一致就会出问题

5年前 评论

@王举 楼主个人觉得你登录这块有问题。你那个登录验证不应该存在,passport本身就要对用户信息做验证所以没必要自己验证。如果要自定义验证子段上面的朋友已经给出了答案。重写api守卫的模型方法

public function findForPassport($username)
{
        return self::where('phone', $username)->first();
}
5年前 评论
颠倒的玉石

@王举 大神我的也是报错

file

fileguard这里自定义了一个表。看到你说的这个方法

file我这样写

file发现仍然报错,这样写他查的是user表了

file

5年前 评论
颠倒的玉石

@Johnson16 按你的写法

file

file

file结果报错,他似乎走的是默认的users表,看守器guard似乎没起作用

file

5年前 评论

@scenery 整个问题的确有些蹊跷,我抽空帮你看看

5年前 评论

请问api接口多版本控制怎么处理?

5年前 评论
王举

@Johnson16 这么耍流氓的么:cry:

5年前 评论
王举

@huzhuolei 通过不同的请求头或者请求链接划分呗

比如: 链接

/api/v1/comments
/api/v2/comments

或者类型请求头

Accept:application/vnd.{API_SUBTYPE}.v2+json
5年前 评论

@王举 哈哈,别告诉别人哈。

5年前 评论

这个地方,需要传一个user_id,关联users表id,如果不绑定user_id,请求获取token会返回:The user credentials were incorrect

file

5年前 评论

您好,我在使用的过程中,发现,laravel中api接口的话,如果设置的独立验证器之类的话。 我们还是需要去针对性的去定制优化。

例如独立验证器具中,我们使用respose的话,直接return, 还是会使用FormRequest的响应规则

所以我在基础上进行修改了一下,您看看怎么样呢?

https://github.com/surest-sky/example/blob...

4年前 评论

如果真的是采取官方passport+API Resource+Gates&Policies采取RESTful API的话实现起来可比这简单多了,作者把流程搞复杂了,代码也没有组织好,逻辑上有些乱了。想要用好Passport,OAuth2.0的基础还是要有的,不然授权表里的东西压根都理解不了的。Coding10 的教程里对API的开发讲解的我觉得是解释的最清楚的,虽然是付费的教程

4年前 评论

我在想ApiResponse有必要放到这么深的目录下吗,或者有没有其他更好的位置可以放

4年前 评论
王举

@被困群众 放到哪无所谓啦,看个人喜好,比如直接放到app下面啥的

4年前 评论

@Thank 虽然我不知道你是否还需要,但可以拦截 这个 Dotenv\Exception\ValidationException; 一样可以捕获到验证层的错误

4年前 评论

我想问一下,这样处理,很多异常假如不放到doReports中的话,经常会报错500,然后没有任何错误信息,这是我的写法,是否有更好的解决方式呢

    public function render($request, Exception $exception)
    {
        if ((request()->wantsJson() || request()->expectsJson())) {
            // 将方法拦截到自己的ExceptionReport
            $reporter = ExceptionRepoet::make($exception);
            if ($reporter->shouldReturn()) {    // 404,401,429, 422
                return $reporter->report();
            } else {
                return $reporter->internalError(parent::render($request, $exception));
            }
        } else {
            return parent::render($request, $exception);
        }
    }

比如一个没放在doReport的错误

{
    "status_code": 500,
    "error_code": -1,
    "message": {
        "headers": {},
        "original": {
            "message": "SQLSTATE[HY000] [1045] Access denied for user 'root'@'localhost' (using password: YES) (SQL: select * from `users` where `user_phone` = 17350221396 limit 1)",
……
4年前 评论
王举

@mingzaily 我是这么处理的 https://github.com/wowiwj/story-server/blo...

系统错误信息还是比较敏感的,默认不会暴露给前端

4年前 评论

@王举 谢谢,我也是第一次碰API,开始看教程用dingp\api,但是相关的异常,根本没有办法重写,把它汉化成中文,又看到你这篇文章,很大的启发

4年前 评论

@王举 我找到一个方法了
在env的APP_DEBUG控制是否显示系统错误信息

    public function render($request, Exception $exception)
    {
        // 中间件判断一定是json请求
        // 将方法拦截到自己的ExceptionReport
        $reporter = ExceptionRepoet::make($exception);
        if ($reporter->shouldReturn()) {    // 404,401,429, 422
            return $reporter->report();
        }

        $message = json_decode(parent::render($request, $exception)->getContent())->message;
        return $reporter->internalError($message);
    }

例子
开启debug

{
    "status_code": 500,
    "error_code": -1,
    "message": "SQLSTATE[HY000] [1045] Access denied for user 'root'@'localhost' (using password: YES) (SQL: select * from `users` where `user_phone` = 17350221396 limit 1)"
}

关闭debug

{
    "status_code": 500,
    "error_code": -1,
    "message": "Server Error"
}
4年前 评论

大佬 ,请教下返回资源集合的时候,隐藏个别字段有比较好的方法吗 :grinning:

4年前 评论
王举

@lun1bz Api Resource 文档去研究一下,写 Api 神器

4年前 评论

@王举 看了好几遍了,没找到合适的办法,返回资源集合我暂时给手动查询返回了,返回单个资源没问题,用了你的这个trait重写了下 :grin:谢谢啦

4年前 评论
dannyvan

@王举 请问你做 token 刷新了吗?这里生成的 token 有效期有一年之久,token 一旦被别人拿去了,就很危险了 :smile:

4年前 评论

@王举 请问如何增加自定义错误码呢,比如我想返回 1001 和 1002 给前端做额外的操作

4年前 评论
Liuzhipeng_laravel 4年前
____ (作者) 4年前

请问前端是通过判断code=200还是success来判断此次请求成功。我看code有时会自定义改变

4年前 评论

@lun1bz

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    protected $showSensitiveFields = false;

    public function toArray($request)
    {
        if (!$this->showSensitiveFields) {
            $this->resource->addHidden(['phone', 'email']);
        }
        return parent::toArray($request);
    }

    public function showSensitiveFields()
    {
        $this->showSensitiveFields = true;
        return $this;
    }
}

//默认隐藏phone、email字段:
//return new UserResource($user);

//显示隐藏字段:
//return (new UserResource($user))->showSensitiveFields();
3年前 评论

file

![file](cdn.learnku.com/uploads/images/202... @王举 大佬,报这个错什么原因

3年前 评论
Rookie! (作者) 3年前

如果返回 success()前端通过 res.code 可以获取状态码,返回 failed()用res.code获取不到需用 res.data.code

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!