Laravel 开发 API

2021-06-04 14:05

阅读:752

标签:des   http   catch   cut   svg   osi   jobs   var   operator   

 

1. 起因

       随着前后端完全分离,PHP 也基本告别了 view 模板嵌套开发,转而专门写资源接口。Laravel 是 PHP 框架中最优雅的框架,国内也越来越多人告别 ThinkPHP 选择了 LaravelLaravel 框架本身对 API 有支持,但是感觉再工作中还是需要再做一些处理。Lumen 用起来不顺手,有些包不能很好地支持。所以,将 Laravel 框架进行一些配置处理,让其在开发 API 时更得心应手。

       当然,你也可以点击这里 , 直接跳到成果~

 

2. 准备工作

 

2.1. 环境

PHP > 7.1
MySQL > 5.5
Redis > 2.8
 

2.2. 工具

postman
composer
 

2.3. 使用 postman

为了模拟 AJAX 请求,请将 header头 设置 X-Requested-With 为 XMLHttpRequest

 

技术图片
 

 

 

2.4. 安装 Laravel

Laravel 只要 >=5.5 皆可,这里采用文章编写时最新的 5.7 版本

composer create-project laravel/laravel Laravel --prefer-dist "5.7.*"
 

2.5. 创建数据库

CREATE TABLE `users` (
    `id` INT UNSIGNED NOT NULL PRIMARY KEY auto_increment COMMENT ‘主键ID‘,
    `name` VARCHAR ( 12 ) NOT NULL COMMENT ‘用户名称‘,
    `password` VARCHAR ( 80 ) NOT NULL COMMENT ‘密码‘,
    `last_token` text COMMENT ‘登陆时的token‘,
    `status` TINYINT NOT NULL DEFAULT 0 COMMENT ‘用户状态 -1代表已删除 0代表正常 1代表冻结‘,
    `created_at` TIMESTAMP NULL DEFAULT NULL COMMENT ‘创建时间‘,
`updated_at` TIMESTAMP NULL DEFAULT NULL COMMENT ‘修改时间‘ 
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci;
 

3. 初始化数据

 

3.1. Model 移动

在项目的 app 目录下可以看到,有一个 User.php 的模型文件。因为 Laravel 默认把模型文件放在 app 目录下,如果数据表多的话,这里模型文件就会很多,不便于管理,所以我们先要将模型文件移动到其他文件夹内。

1) 在 app 目录下新建 Models 文件夹,然后将 User.php 文件移动进来。
2) 修改 User.php 的内容

namespace App\Models; //这里从App改成了App\Models

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;
    protected $table = ‘users‘;

     //去掉我创建的数据表没有的字段
    protected $fillable = [
        ‘name‘, ‘password‘
    ];

     //去掉我创建的数据表没有的字段
    protected $hidden = [
        ‘password‘
    ];
    //将密码进行加密
    public function setPasswordAttribute($value)
    {
        $this->attributes[‘password‘] = bcrypt($value);
    }
}

3) 因为有关于 User 的命名空间发生了改变,所以我们全局搜索 App\User, 将其替换为 App\Models\User. 我一共搜索到 4 个文件

app/Http/Controllers/Auth 目录下的 RegisterController.php
config 目录下的 services.php
config 目录下的 auth.php
database/factories 目录下的 UserFactory.php
 

3.2. 控制器

因为是专门做 API 的,所以我们要把是 API 的控制器都放到 app\Http\Controllers\Api 目录下。

使用命令行创建控制器

php artisan make:controller Api/UserController

编写 app/Http/Controllers/Api 目录下的 UserController.php 文件

namespace App\Http\Controllers\Api;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    //
    public function index(){
        return ‘guaosi‘;
    }
}

这里写了 index 函数,用来下面建立路由后的测试,查看是否可以正常访问。

 

3.3. 路由

在 routes 目录下的 api.php 是专门用来写 Api 接口的路由,所以我们打开它,填写以下内容,做一个测试.

use Illuminate\Http\Request;

Route::namespace(‘Api‘)->prefix(‘v1‘)->group(function () {
        Route::get(‘/users‘,‘UserController@index‘)->name(‘users.index‘);
});

因为我们 Api 控制器的命名空间是 App\Http\Controllers\Api, 而 Laravel 默认只会在命名空间 App\Http\Controllers 下查找控制器,所以需要我们给出 namespace

同时,添加一个 prefix 是为了版本号,方便后期接口升级区分。

打开 postman, 用 get 方式请求你的域名/api/v1/users, 最后返回结果是

guaosi

则成功

 

3.4. 创建验证器

在创建用户之前,我们先创建验证器,来让我们服务器接收到的数据更安全。当然,我们也要把关于 Api 验证的放在一个专门的文件夹内。
先创建一个 Request 的基类

php artisan make:request Api/FormRequest

因为验证器默认的权限验证是 false,导致返回都是 403 的权限不通过错误。这里我们没有用到权限认证,为了方便处理,我们默认将权限都是通过的状态。所以,每个文件都需要我们将 false 改成 true

public function authorize()
{
//false代表权限验证不通过,返回403错误
//true代表权限认证通过
return true;
}

所以我们修改 app/Http/Requests/Api 目录下的 FormRequest.php 文件

namespace App\Http\Requests\Api;

use Illuminate\Foundation\Http\FormRequest as BaseFormRequest;

class FormRequest extends BaseFormRequest
{
    public function authorize()
    {
        //false代表权限验证不通过,返回403错误
        //true代表权限认证通过
        return true;
    }
}

这样这个命名空间下的验证器都会默认通过权限验证。当然,如果你需要权限验证,可以通过直接覆盖方法。

接着我们开始创建关于 UserController 的专属验证器

php artisan make:request Api/UserRequest

编辑 app/Http/Requests/Api 目录下的 UserRequest.php 文件

namespace App\Http\Requests\Api;

class UserRequest extends FormRequest
{
    public function rules()
    {

        switch ($this->method()) {
            case ‘GET‘:
                {
                    return [
                        ‘id‘ => [‘required,exists:shop_user,id‘]
                    ];
                }
            case ‘POST‘:
                {
                    return [
                        ‘name‘ => [‘required‘, ‘max:12‘, ‘unique:users,name‘],
                        ‘password‘ => [‘required‘, ‘max:16‘, ‘min:6‘]
                    ];
                }
            case ‘PUT‘:
            case ‘PATCH‘:
            case ‘DELETE‘:
            default:
                {
                    return [

                    ];
                }
        }
    }

    public function messages()
    {
        return [
            ‘id.required‘=>‘用户ID必须填写‘,
            ‘id.exists‘=>‘用户不存在‘,
            ‘name.unique‘ => ‘用户名已经存在‘,
            ‘name.required‘ => ‘用户名不能为空‘,
            ‘name.max‘ => ‘用户名最大长度为12个字符‘,
            ‘password.required‘ => ‘密码不能为空‘,
            ‘password.max‘ => ‘密码长度不能超过16个字符‘,
            ‘password.min‘ => ‘密码长度不能小于6个字符‘
        ];
    }
}
 

3.5. 创建用户

现在我们来编写创建用户接口,制作一些虚拟数据。(就不使用 seeder 来填充了)
打开 UserController.php

//用户注册
public function store(UserRequest $request){
    User::create($request->all());
    return ‘用户注册成功。。。‘;
}
//用户登录
public function login(Request $request){
    $res=Auth::guard(‘web‘)->attempt([‘name‘=>$request->name,‘password‘=>$request->password]);
    if($res){
        return ‘用户登录成功...‘;
     }
    return ‘用户登录失败‘;
}

然后我们创建路由,编辑 api.php

Route::post(‘/users‘,‘UserController@store‘)->name(‘users.store‘);
Route::post(‘/login‘,‘UserController@login‘)->name(‘users.login‘);

打开 postman, 用 post 方式请求你的域名/api/v1/users, 在 form-data 记得填写要创建的用户名和密码。

最后返回结果是

用户创建成功。。。

则成功。

 

技术图片
 

 

如果返回

{
    "message": "The given data was invalid.",
    "errors": {
        "name": [
            "用户名不能为空"
        ],
        "password": [
            "密码不能为空"
        ]
    }
}

则证明验证失败。

然后验证是否可以正常登录。因为我们认证的字段是 name 跟 password, 而 Laravel 默认认证的是 email跟 password。所以我们还要打开 app/Http/Controllers/auth 目录下的 LoginController.php, 加入如下代码

public function username()
{
    return ‘name‘;
}

打开 postman, 用 post 方式请求你的域名/api/v1/login
最后返回结果是

用户登录成功...

则成功

 

技术图片
 

 

 

3.6. 创建 10 个用户

为了测试使用,请自行通过接口创建 10 个用户。

 

3.7. 编写相关资源接口

给出整体控制器信息 UserController.php

namespace App\Http\Controllers\Api;

use App\Http\Requests\Api\UserRequest;
use App\Models\User;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class UserController extends Controller
{

    //返回用户列表
    public function index(){
        //3个用户为一页
        $users = User::paginate(3);
        return $users;
    }
    //返回单一用户信息
    public function show(User $user){
        return $user;
    }
    //用户注册
    public function store(UserRequest $request){
        User::create($request->all());
        return ‘用户注册成功。。。‘;
    }
    //用户登录
    public function login(Request $request){
        $res=Auth::guard(‘web‘)->attempt([‘name‘=>$request->name,‘password‘=>$request->password]);
        if($res){
            return ‘用户登录成功...‘;
        }
        return ‘用户登录失败‘;
    }
}
 

3.8. 编写路由

给出整体路由信息 api.php

use Illuminate\Http\Request;

Route::namespace(‘Api‘)->prefix(‘v1‘)->group(function () {
        Route::get(‘/users‘,‘UserController@index‘)->name(‘users.index‘);
        Route::get(‘/users/{user}‘,‘UserController@show‘)->name(‘users.show‘);
        Route::post(‘/users‘,‘UserController@store‘)->name(‘users.store‘);
        Route::post(‘/login‘,‘UserController@login‘)->name(‘users.login‘);
});
 

4. 存在问题

以上所有返回的结果,无论正确或者错误,都没有一个统一格式规范,对开发 Api 不太友好的,需要我们进行一些修改,让 Laravel 框架可以更加友好地编写 Api。

 

5. 构造

 

5.1. 跨域问题

所有问题,跨域先行。跨域问题没有解决,一切处理都是纸老虎。这里我们使用 medz 做的 cors 扩展包

 

5.1.1. 安装 medz/cors

composer require medz/cors
 

5.1.2. 发布配置文件

php artisan vendor:publish --provider="Medz\Cors\Laravel\Providers\LaravelServiceProvider" --force
 

5.1.3. 修改配置文件

打开 config/cors.php, 在 expose-headers 添加值 Authorization

return [
    ......
    ‘expose-headers‘     => [‘Authorization‘],
    ......
];

这样跨域请求时,才能返回 header 头为 Authorization 的内容,否则在刷新用户 token 时不会返回刷新后的 token

 

5.1.4. 增加中间件别名

打开 app/Http/Kernel.php, 增加一行

protected $routeMiddleware = [
        ...... //前面的中间件
        ‘cors‘=> \Medz\Cors\Laravel\Middleware\ShouldGroup::class,
];
 

5.1.5. 修改路由

打开 routes/api.php, 在路由组中增加使用中间件

Route::namespace(‘Api‘)->prefix(‘v1‘)->middleware(‘cors‘)->group(function () {
        Route::get(‘/users‘,‘UserController@index‘)->name(‘users.index‘);
        Route::get(‘/users/{user}‘,‘UserController@show‘)->name(‘users.show‘);
        Route::post(‘/users‘,‘UserController@store‘)->name(‘users.store‘);
        Route::post(‘/login‘,‘UserController@login‘)->name(‘users.login‘);
});
 

5.2. 统一 Response 响应处理

接口主流返回 json 格式,其中包含 http状态码status请求状态data请求资源结果等等。需要我们有一个 API 接口全局都能有统一的格式和对应的数据处理。参考于这里。

 

5.2.1. 封装返回的统一消息

在 app/Api/Helpers 目录 (不存在目录自己新建) 下新建 ApiResponse.php
填入如下内容

namespace App\Api\Helpers;
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,$httpCode=null)
    {
        $httpCode = $httpCode ?? $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
     */
    /*
     * 格式
     * data:
     *  code:422
     *  message:xxx
     *  status:‘error‘
     */
    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


评论


亲,登录后才可以留言!