手摸手教你让Laravel开发Api更得心应手 Posted 2021-03-21 onewaa
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手摸手教你让Laravel开发Api更得心应手相关的知识,希望对你有一定的参考价值。
https://www.guaosi.com/2019/02/26/laravel-api-initialization-preparation/
1. 起因
随着前后端完全分离,php
也基本告别了view
模板嵌套开发,转而专门写资源接口。Laravel
是PHP框架中最优雅的框架,国内也越来越多人告别ThinkPHP
选择了Laravel
。Laravel
框架本身对API
有支持,但是感觉再工作中还是需要再做一些处理。Lumen
用起来不顺手,有些包不能很好地支持。所以,将Laravel
框架进行一些配置处理,让其在开发API
时更得心应手。
内容划水过长,请谨慎打开
当然,你也可以点击这里 ,直接跳到成果~
2. 准备工作
2.1. 环境
123
PHP > 7.1mysql > 5.5Redis > 2.8
2.2. 工具
2.3. 使用postman
为了模拟AJAX请求,请将 header头
设置X-Requested-With
为 XMLHttpRequest
2.4. 安装Laravel
Laravel
只要>=5.5
皆可,这里采用文章编写时最新的5.7
版本
1
composer create-project laravel/laravel Laravel --prefer-dist "5.7.*"
2.5. 创建数据库
123456789
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
的内容
123456789101112131415161718192021222324252627
<?phpnamespace AppModels;
3) 因为有关于User的命名空间发生了改变,所以我们全局搜索AppUser
,将其替换为AppModelsUser
.我一共搜索到3个文件
1234
app/Http/Controllers/Auth 目录下的 RegisterController.phpconfig 目录下的 services.phpconfig 目录下的 auth.phpdatabase/factories 目录下的 UserFactory.php
3.2. 控制器
因为是专门做API的,所以我们要把是API的控制器都放到appHttpControllersApi
目录下。
使用命令行创建控制器
1
php artisan make:controller Api/UserController
编写app/Http/Controllers/Api
目录下的UserController.php
文件
1234567891011121314
<?phpnamespace AppHttpControllersApi;use IlluminateHttpRequest;use AppHttpControllersController;class UserController extends Controller{
这里写了index函数,用来下面建立路由后的测试,查看是否可以正常访问。
3.3. 路由
在routes
目录下的api.php
是专门用来写Api接口的路由,所以我们打开它,填写以下内容,做一个测试.
123456
<?phpuse IlluminateHttpRequest;Route::namespace(‘Api‘)->prefix(‘v1‘)->group(function () { Route::get(‘/users‘,‘UserController@index‘)->name(‘users.index‘);});
因为我们Api控制器的命名空间是AppHttpControllersApi
,而Laravel
默认只会在命名空间AppHttpControllers
下查找控制器,所以需要我们给出namespace
。
同时,添加一个prefix
是为了版本号,方便后期接口升级区分。
打开postman
,用get
方式请求你的域名/api/v1/users
,最后返回结果是
则成功
3.4. 创建验证器
在创建用户之前,我们先创建验证器,来让我们服务器接收到的数据更安全.当然,我们也要把关于Api验证的放在一个专门的文件夹内。 先创建一个Request
的基类
1
php artisan make:request Api/FormRequest
因为验证器默认的权限验证是false
,导致返回都是403
的权限不通过错误。这里我们没有用到权限认证,为了方便处理,我们默认将权限都是通过的状态。所以,每个文件都需要我们将false
改成true
。
123456
public function authorize(){
所以我们修改app/Http/Requests/Api
目录下的 FormRequest.php
文件
123456789101112131415
<?phpnamespace AppHttpRequestsApi;use IlluminateFoundationHttpFormRequest as BaseFormRequest;class FormRequest extends BaseFormRequest{ public function authorize() {
这样这个命名空间下的验证器都会默认通过权限验证。当然,如果你需要权限验证,可以通过直接覆盖方法。
接着我们开始创建关于UserController
的专属验证器
1
php artisan make:request Api/UserRequest
编辑app/Http/Requests/Api
目录下的 UserRequest.php
文件
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
<?phpnamespace AppHttpRequestsApi;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
然后我们创建路由,编辑api.php
12
Route::post(‘/users‘,‘UserController@store‘)->name(‘users.store‘);Route::post(‘/login‘,‘UserController@login‘)->name(‘users.login‘);
打开postman
,用post
方式请求你的域名/api/v1/users
,在form-data
记得填写要创建的用户名和密码。
最后返回结果是
则成功。
如果返回
1234567891011
{ "message": "The given data was invalid.", "errors": { "name": [ "用户名不能为空" ], "password": [ "密码不能为空" ] }}
则证明验证失败。
然后验证是否可以正常登录。因为我们认证的字段是name
跟password
,而Laravel
默认认证的是email
跟password
。所以我们还要打开app/Http/Controllers/auth
目录下的 LoginController.php
,加入如下代码
1234
public function username() { return ‘name‘;}
打开postman
,用post
方式请求你的域名/api/v1/login
最后返回结果是
则成功
3.6. 创建10个用户
为了测试使用,请自行通过接口创建10个用户。
3.7. 编写相关资源接口
给出整体控制器信息UserController.php
12345678910111213141516171819202122232425262728293031323334353637
<?phpnamespace AppHttpControllersApi;use AppHttpRequestsApiUserRequest;use AppModelsUser;use AppHttpControllersController;use IlluminateHttpRequest;use IlluminateSupportFacadesAuth;class UserController extends Controller{
3.8. 编写路由
给出整体路由信息api.php
123456789
<?phpuse IlluminateHttpRequest;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
1
composer require medz/cors
5.1.2. 发布配置文件
1
php artisan vendor:publish --provider="MedzCorsLaravelProvidersLaravelServiceProvider" --force
5.1.3. 修改配置文件
打开config/cors.php
,在expose-headers
添加值Authorization
12345
return [ ...... ‘expose-headers‘ => [‘Authorization‘], ......];
这样跨域请求时,才能返回header
头为Authorization
的内容,否则在刷新用户token
时不会返回刷新后的token
5.1.4. 增加中间件别名
打开app/Http/Kernel.php
,增加一行
1234
protected $routeMiddleware = [ ......
5.1.5. 修改路由
打开routes/api.php
,在路由组中增加使用中间件
123456
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
填入如下内容
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
<?phpnamespace AppApiHelpers;use SymfonyComponentHttpFoundationResponse 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($message,FoundationResponse::HTTP_INTERNAL_SERVER_ERROR); } /**