larvel 中的api.php_Laravel API 系列教程(一): 基于 Laravel 5.5 构建 测试 RESTful API...
Laravel API 系列教程(一): 基于 Laravel 5.5 構建 & 測試 RESTful API
由 學院君 創建于2年前, 最后更新于 9個月前
版本號 #3
171702 views
51 likes
2 collects
隨著移動開發和 JavaScript 框架的日益流行,使用 RESTful API 在數據層和客戶端之間構建交互接口逐漸成為最佳選擇。在本系列教程中,學院君將會帶領大家基于 Laravel 5.5 來構建并測試帶認證功能的 RESTful API。
RESTful API
開始之前,我們先要了解什么是 RESTful API。REST 是 REpresentational State Transfer 的縮寫,表示一種應用之間網絡通信的架構風格,依賴于無狀態的協議(通常是HTTP)進行交互。
通過 HTTP 動詞表示操作
在 RESTful API 中,我們使用 HTTP 動詞表示操作,而端點是操作的資源,HTTP 動詞的語義如下:
GET:獲取資源
POST:創建資源
PUT:更新資源
DELETE:刪除資源
更新操作:PUT vs. POST
關于 RESTful API 有很多爭議,比如更新資源使用 POST、PATCH 還是 PUT 哪一個更好,或者創建資源是否最好用 PUT 等。在本教程中,我們使用 PUT 進行更新操作,因為基于 HTTP RFC 標準,PUT 的含義是在指定位置上創建/更新資源;使用 PUT 的另一個原因是冪等,這意味著不管你發送一次、兩次還是上千次請求,操作結果一致。
資源
資源指的是操作的對象,在我們的例子中就是文章(Articles)和用戶(Users),它們各自的端點是:
/articles
/users
在我們的教程中,資源和數據模型一一對應,但這并不是強制性的要求,一個資源可以表示多個數據模型(或者不代表數據庫中的任何模型),你可以根據自己應用的需求來決定如何構建資源和模型的映射關系。
關于一致性的注意項
使用 REST 的最大好處是可以更容易消費和開發 API,一些端點非常直截了當,這樣相較于類似 GET /get_article?id_article=12 這樣的端點 RESTful API 更容易使用和維護。
不過,在某些案例中映射到 Create/Retrieve/Update/Delete 可能會很困難,需要牢記的是 URL 中不要包含任何動詞而且資源并不一定非得是數據表的某一行數據。另一個需要記住的是不必為每個資源實現所有操作。
設置一個新的 Laravel 項目
創建新應用
我們通過 Composer 來安裝這個新項目:
composer create-project --prefer-dist laravel/laravel apidemo 5.5.*
安裝完成后,如果使用 Valet 作為開發環境的話就可以直接在瀏覽器中通過 http://apidemo.test 訪問了,如果使用 Laradock 或 Homestead 作為開發環境的話,還需要配置域名綁定:
127.0.0.1 apidemo.test
然后還要配置 Nginx 的配置文件,以 Laradock 為例,就是在 laradock/nginx/sites 目錄下新建一個 apidemo.conf 配置文件,編輯其內容如下:
server {
listen 80;
listen [::]:80;
server_name apidemo.test;
root /var/www/apidemo/public;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri /index.php =404;
fastcgi_pass php-upstream;
fastcgi_index index.php;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
#fixes timeouts
fastcgi_read_timeout 600;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
location /.well-known/acme-challenge/ {
root /var/www/letsencrypt/;
log_not_found off;
}
error_log /var/log/nginx/apidemo_error.log;
access_log /var/log/nginx/apidemo_access.log;
}
之后重新啟動 Nginx:
docker-compose up --build -d nginx
就可以在瀏覽器中通過 http://apidemo.test 訪問應用了:
遷移和模型
在編寫第一個遷移之前,需要將 .env 文件中的環境變量調整為開發環境數據庫配置值(以 Laradock 為例):
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=apidemo
DB_USERNAME=root
DB_PASSWORD=root
接下來就可以開始創建我們的第一個 Article 模型及其對應遷移文件了,我們在項目根目錄運行如下 Artisan 命令一步到位:
php artisan make:model Article -m
-m 是 --migration 的縮寫,告知 Artisan 在創建模型同時創建與之對應的遷移文件(我使用的是 Laradock 作為開發環境):
當然,還需要編輯默認生成的遷移文件:
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateArticlesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('articles', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('body');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('articles');
}
}
然后我們運行如下命令創建對應數據表:
php artisan migrate
現在我們回到 Article 模型類添加如下屬性到 $fillable 字段以便可以在 Article::create 和 Article::update 方法中可以使用它們:
class Article extends Model
{
protected $fillable = ['title', 'body'];
}
數據庫填充
Laravel 通過 Faker 庫可以快速為我們生成格式正確的測試數據:
php artisan make:seeder ArticlesTableSeeder
生成的填充器類位于 /database/seeds 目錄下,我們編輯填充器類如下:
use Illuminate\Database\Seeder;
use App\Article;
class ArticlesTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// Let's truncate our existing records to start from scratch.
Article::truncate();
$faker = \Faker\Factory::create();
// And now, let's create a few articles in our database:
for ($i = 0; $i < 50; $i++) {
Article::create([
'title' => $faker->sentence,
'body' => $faker->paragraph,
]);
}
}
}
然后運行填充命令:
php artisan db:seed --class=ArticlesTableSeeder
重復上述過程創建一個用戶填充器:
use Illuminate\Database\Seeder;
use App\User;
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// Let's clear the users table first
User::truncate();
$faker = \Faker\Factory::create();
// Let's make sure everyone has the same password and
// let's hash it before the loop, or else our seeder
// will be too slow.
$password = Hash::make('toptal');
User::create([
'name' => 'Administrator',
'email' => 'admin@test.com',
'password' => $password,
]);
// And now let's generate a few dozen users for our app:
for ($i = 0; $i < 10; $i++) {
User::create([
'name' => $faker->name,
'email' => $faker->email,
'password' => $password,
]);
}
}
}
編輯 DatabaseSeeder 類:
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$this->call(UsersTableSeeder::class);
$this->call(ArticlesTableSeeder::class);
}
}
然后運行 php artisan db:seed 就可以執行所有填充器填充數據。
路由和控制器
注冊路由
有了數據之后,接下來我們來為應用創建基本接口:創建、獲取列表、獲取單條記錄、更新以及刪除。在 routes/api.php 中,注冊路由如下:
use Illuminate\Http\Request;
use App\Article;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
Route::get('articles', function() {
// If the Content-Type and Accept headers are set to 'application/json',
// this will return a JSON structure. This will be cleaned up later.
return Article::all();
});
Route::get('articles/{id}', function($id) {
return Article::find($id);
});
Route::post('articles', function(Request $request) {
return Article::create($request->all);
});
Route::put('articles/{id}', function(Request $request, $id) {
$article = Article::findOrFail($id);
$article->update($request->all());
return $article;
});
Route::delete('articles/{id}', function($id) {
Article::find($id)->delete();
return 204;
});
在 api.php 中定義的路由在訪問時需要加上 /api/ 前綴,并且 API 限流中間件會自動應用到所有路由上:
創建控制器
定義好路由之后,接下來我們來創建控制器并將業務邏輯遷移過去:
php artisan make:controller ArticleController
然后編輯控制器如下:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Article;
class ArticleController extends Controller
{
public function index()
{
return Article::all();
}
public function show($id)
{
return Article::find($id);
}
public function store(Request $request)
{
return Article::create($request->all());
}
public function update(Request $request, $id)
{
$article = Article::findOrFail($id);
$article->update($request->all());
return $article;
}
public function delete(Request $request, $id)
{
$article = Article::findOrFail($id);
$article->delete();
return 204;
}
}
調整 routes/api.php 文件中的 articles 相關路由:
Route::get('articles', 'ArticleController@index');
Route::get('articles/{id}', 'ArticleController@show');
Route::post('articles', 'ArticleController@store');
Route::put('articles/{id}', 'ArticleController@update');
Route::delete('articles/{id}', 'ArticleController@delete');
還可以通過隱式路由模型綁定來改寫路由定義:
Route::get('articles', 'ArticleController@index');
Route::get('articles/{article}', 'ArticleController@show');
Route::post('articles', 'ArticleController@store');
Route::put('articles/{article}', 'ArticleController@update');
Route::delete('articles/{article}', 'ArticleController@delete');
相應的,需要調整控制器代碼:
class ArticleController extends Controller
{
public function index()
{
return Article::all();
}
public function show(Article $article)
{
return $article;
}
public function store(Request $request)
{
$article = Article::create($request->all());
return response()->json($article, 201);
}
public function update(Request $request, Article $article)
{
$article->update($request->all());
return response()->json($article, 200);
}
public function delete(Article $article)
{
$article->delete();
return response()->json(null, 204);
}
}
訪問指定路由與之前返回結果一樣:
關于 HTTP 狀態碼的注意項
我們還添加了 response()->json() 調用到端點,這可以讓我們在顯示返回 JSON 數據的同時發送可以被客戶端解析的 HTTP 狀態碼,最常用的狀態碼如下:
200:OK,標準的響應成功狀態碼
201:Object created,用于 store 操作
204:No content,操作執行成功,但是沒有返回任何內容
206:Partial content,返回部分資源時使用
400:Bad request,請求驗證失敗
401:Unauthorized,用戶需要認證
403:Forbidden,用戶認證通過但是沒有權限執行該操作
404:Not found,請求資源不存在
500:Internal server error,通常我們并不會顯示返回這個狀態碼,除非程序異常中斷
503:Service unavailable,一般也不會顯示返回,通常用于排查問題用
發送 404 響應
如果你試圖獲取不存在的資源,會返回 404 頁面:
如果想要將其改造成返回 JSON 響應,可以編輯異常處理器 app/Exceptions/Handler.php 的 render 方法:
public function render($request, Exception $exception)
{
if ($exception instanceof ModelNotFoundException) {
return response()->json([
'error' => 'Resource not found.'
],404);
}
return parent::render($request, $exception);
}
再次訪問不存在的資源,返回結果如下:
認證
新增 api_token 字段
在 Laravel 中實現 API 認證有多種方式(例如 Passport),但是本教程會使用一個非常簡化的方式。
開始之前,首先添加 api_token 到 users 表:
php artisan make:migration --table=users adds_api_token_to_users_table
然后編寫這個遷移文件:
class AddsApiTokenToUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('api_token', 60)->unique()->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['api_token']);
});
}
}
最后執行遷移命令作用于數據表:
php artisan migrate
創建注冊接口
我們使用 RegisterController 來根據注冊請求返回正確的響應。盡管 Laravel 開箱提供了認證功能,但是我們還是需要對其進行調整以便返回我們想要的響應數據。該控制器使用 RegistersUsers 來實現注冊,實現邏輯如下:
public function register(Request $request)
{
// Here the request is validated. The validator method is located
// inside the RegisterController, and makes sure the name, email
// password and password_confirmation fields are required.
$this->validator($request->all())->validate();
// A Registered event is created and will trigger any relevant
// observers, such as sending a confirmation email or any
// code that needs to be run as soon as the user is created.
event(new Registered($user = $this->create($request->all())));
// After the user is created, he's logged in.
$this->guard()->login($user);
// And finally this is the hook that we want. If there is no
// registered() method or it returns null, redirect him to
// some other URL. In our case, we just need to implement
// that method to return the correct response.
return $this->registered($request, $user)
?: redirect($this->redirectPath());
}
我們只需要在 RegisterController 中實現 registered 方法即可。該方法接收 $request 和 $user 參數:
protected function registered(Request $request, $user)
{
$user->generateToken();
return response()->json(['data' => $user->toArray()], 201);
}
在 routes/api.php 中注冊路由如下:
Route::post('register', 'Auth\RegisterController@register');
在上面的示例代碼中,我們調用了 User 模型上的生成令牌方法,該方法現在不存在,需要手動添加:
public function generateToken()
{
$this->api_token = str_random(60);
$this->save();
return $this->api_token;
}
至此,注冊接口編寫完成,用戶現在可以通過注冊接口進行注冊了,感謝 Laravel 開箱提供的認證字段驗證功能,如果你需要調整驗證規則的話可以到 RegisterController 中查看 validator 方法。
下面我們來簡單測試下注冊接口:
curl -X POST http://apidemo.test/api/register \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"name": "學院君", "email": "admin@laravelacademy.org", "password": "test123", "password_confirmation": "test123"}'
創建登錄接口
和注冊接口一樣,可以編輯 LoginController 控制器來支持 API 認證。為此,我們需要在 LoginController 覆蓋 AuthenticatesUsers trait 提供的 login 方法:
public function login(Request $request)
{
$this->validateLogin($request);
if ($this->attemptLogin($request)) {
$user = $this->guard()->user();
$user->generateToken();
return response()->json([
'data' => $user->toArray(),
]);
}
return $this->sendFailedLoginResponse($request);
}
然后在 routes/api.php 中注冊登錄路由:
Route::post('login', 'Auth\LoginController@login');
現在,基于我們上面注冊的新用戶,我們來測試下登錄接口:
curl -X POST http://apidemo.test/api/login \
-H "Accept: application/json" \
-H "Content-type: application/json" \
-d "{\"email\": \"admin@laravelacademy.org\", \"password\": \"test123\" }"
登錄成功返回結果:
后面就可以拿著這個 api_token 作為令牌來請求需要認證的資源了。使用我們現有的策略,請求認證資源時,如果沒有 token 或 token 錯誤,用戶將會接收到未認證響應(401)。
創建退出接口
為了形成完整閉環,下面我們來編寫退出登錄接口,實現思路是用戶發起退出登錄請求時,我們將其對應的 token 字段值從數據庫移除。
首先,在 routes/api.php 中注冊路由:
Route::post('logout', 'Auth\LoginController@logout');
然后在 Auth\LoginController.php 中編寫 logout 方法:
public function logout(Request $request)
{
$user = Auth::guard('api')->user();
if ($user) {
$user->api_token = null;
$user->save();
}
return response()->json(['data' => 'User logged out.'], 200);
}
使用該策略,一旦退出,用戶的所有令牌都會失效,訪問需要認證的接口都會拒絕訪問(通過中間件實現),這需要和前端配合來避免用戶在沒有訪問任何內容的權限下保持登錄狀態。
使用中間件限制訪問
api_token 創建之后,我們就可以在路由文件中應用認證中間件了:
Route::middleware('auth:api')
->get('/user', function (Request $request) {
return $request->user();
});
我們可以使用 $request->user() 或 Auth 門面訪問當前用戶:
Auth::guard('api')->user(); // 登錄用戶實例
Auth::guard('api')->check(); // 用戶是否登錄
Auth::guard('api')->id(); // 登錄用戶ID
接下來,我們將之前定義的文章相關路由進行分組:
Route::group(['middleware' => 'auth:api'], function() {
Route::get('articles', 'ArticleController@index');
Route::get('articles/{article}', 'ArticleController@show');
Route::post('articles', 'ArticleController@store');
Route::put('articles/{article}', 'ArticleController@update');
Route::delete('articles/{article}', 'ArticleController@delete');
});
這樣就不需要為每個路由設置中間件,現在看來雖然節省不了多少時間,但隨著應用體量的增長,這樣做的好處是保持路由的DRY(Don't Repeat Yourself)。
再訪問文章接口就需要認證了:
測試接口
初始化設置
Laravel 開箱集成了 PHPUnit 進行測試,并且在項目根目錄下為我們配置好了 phpunit.xml。本教程中,我們使用內置的測試方法來測試上面編寫的 API。
開始之前,我們需要做一些小調整以便使用內存級的 SQLite 數據庫進行數據存儲。這樣做的好處是可以讓測試更快運行,但缺點是某些遷移命令可能不能正常運行,我的建議是當你遇到運行遷移命令出錯或者更傾向于更加健壯的測試而不是高性能時不要使用 SQLite。
我們還會在每個測試之前運行遷移,這樣就可以為每次測試構建數據庫然后銷毀掉,從而避免不同組測試間的相互干擾。
在 config/database.php 文件中,設置 sqlite 配置項中的 database 字段值為 :memory::
...
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
],
...
]
然后在 phpunit.xml 中通過新增 DB_CONNECTION 環境變量來啟用 SQLite:
基本配置已經完成,生下來要做的就是配置 TestCase 在每次測試前運行遷移并填充數據庫。為此,我們需要添加 DatabaseMigrations trait 然后在 setUp() 方法中添加 Artisan 調用:
namespace Tests;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Illuminate\Support\Facades\Artisan;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication, DatabaseMigrations;
public function setUp()
{
parent::setUp();
Artisan::call('db:seed');
}
}
最后一件事就是添加測試命令到 composer.json:
"scripts": {
"test" : [
"vendor/bin/phpunit"
],
...
},
我們通過這個命令來運行測試:
composer test
如果運行過程中拋出異常:
Error: Class 'Doctrine\DBAL\Driver\PDOSqlite\Driver' not found
這是因為沒有安裝 doctrine/dbal 擴展包,使用 Composer 安裝即可:
composer require doctrine/dbal
下面是運行結果:
為測試設置模型工廠
模型工廠可以讓我們快速生成測試數據,Laravel 開箱自帶了 User 模型工廠,下面我們為 Article 類添加工廠:
php artisan make:factory ArticleFactory
然后編輯 ArticleFactory 類:
use Faker\Generator as Faker;
use App\Article;
$factory->define(Article::class, function (Faker $faker) {
return [
'title' => $faker->sentence,
'body' => $faker->paragraph,
];
});
編寫測試用例
我們可以使用 Laravel 的斷言方法對請求和響應進行測試。下面我們來創建第一個測試用例 —— 登錄測試:
php artisan make:test LoginTest
編寫 LoginTest 代碼如下:
class LoginTest extends TestCase
{
public function testRequiresEmailAndLogin()
{
$this->json('POST', 'api/login')
->assertStatus(422)
->assertJson([
'email' => ['The email field is required.'],
'password' => ['The password field is required.'],
]);
}
public function testUserLoginsSuccessfully()
{
$user = factory(User::class)->create([
'email' => 'test@laravelacademy.org',
'password' => bcrypt('test123'),
]);
$payload = ['email' => 'test@laravelacademy.org', 'password' => 'test123'];
$this->json('POST', 'api/login', $payload)
->assertStatus(200)
->assertJsonStructure([
'data' => [
'id',
'name',
'email',
'created_at',
'updated_at',
'api_token',
],
]);
}
}
然后編寫注冊測試用例:
php artisan make:test RegisterTest
編寫 RegisterTest 代碼如下:
class RegisterTest extends TestCase
{
public function testsRegistersSuccessfully()
{
$payload = [
'name' => 'John',
'email' => 'john@toptal.com',
'password' => 'toptal123',
'password_confirmation' => 'toptal123',
];
$this->json('post', '/api/register', $payload)
->assertStatus(201)
->assertJsonStructure([
'data' => [
'id',
'name',
'email',
'created_at',
'updated_at',
'api_token',
],
]);;
}
public function testsRequiresPasswordEmailAndName()
{
$this->json('post', '/api/register')
->assertStatus(422)
->assertJson([
'name' => ['The name field is required.'],
'email' => ['The email field is required.'],
'password' => ['The password field is required.'],
]);
}
public function testsRequirePasswordConfirmation()
{
$payload = [
'name' => 'John',
'email' => 'john@toptal.com',
'password' => 'toptal123',
];
$this->json('post', '/api/register', $payload)
->assertStatus(422)
->assertJson([
'password' => ['The password confirmation does not match.'],
]);
}
}
最后,編寫退出測試用例:
php artisan make:test LogoutTest
編輯 LogoutTest 代碼如下:
class LogoutTest extends TestCase
{
public function testUserIsLoggedOutProperly()
{
$user = factory(User::class)->create(['email' => 'user@test.com']);
$token = $user->generateToken();
$headers = ['Authorization' => "Bearer $token"];
$this->json('get', '/api/articles', [], $headers)->assertStatus(200);
$this->json('post', '/api/logout', [], $headers)->assertStatus(200);
$user = User::find($user->id);
$this->assertEquals(null, $user->api_token);
}
public function testUserWithNullToken()
{
// Simulating login
$user = factory(User::class)->create(['email' => 'user@test.com']);
$token = $user->generateToken();
$headers = ['Authorization' => "Bearer $token"];
// Simulating logout
$user->api_token = null;
$user->save();
$this->json('get', '/api/articles', [], $headers)->assertStatus(401);
}
}
注:在測試期間,Laravel 應用并不會在發起新請求時再次初始化,所以會在請求之間保存當前用戶到 TokenGuard 實例,也因此我們不得不將退出測試一分為二,以避免受之前緩存用戶的影響。
測試文章 API 接口的代碼也很簡單:
php artisan make:test ArticleTest
編寫 ArticleTest 代碼如下:
class ArticleTest extends TestCase
{
public function testsArticlesAreCreatedCorrectly()
{
$user = factory(User::class)->create();
$token = $user->generateToken();
$headers = ['Authorization' => "Bearer $token"];
$payload = [
'title' => 'Lorem',
'body' => 'Ipsum',
];
$this->json('POST', '/api/articles', $payload, $headers)
->assertStatus(200)
->assertJson(['id' => 1, 'title' => 'Lorem', 'body' => 'Ipsum']);
}
public function testsArticlesAreUpdatedCorrectly()
{
$user = factory(User::class)->create();
$token = $user->generateToken();
$headers = ['Authorization' => "Bearer $token"];
$article = factory(Article::class)->create([
'title' => 'First Article',
'body' => 'First Body',
]);
$payload = [
'title' => 'Lorem',
'body' => 'Ipsum',
];
$response = $this->json('PUT', '/api/articles/' . $article->id, $payload, $headers)
->assertStatus(200)
->assertJson([
'id' => 1,
'title' => 'Lorem',
'body' => 'Ipsum'
]);
}
public function testsArtilcesAreDeletedCorrectly()
{
$user = factory(User::class)->create();
$token = $user->generateToken();
$headers = ['Authorization' => "Bearer $token"];
$article = factory(Article::class)->create([
'title' => 'First Article',
'body' => 'First Body',
]);
$this->json('DELETE', '/api/articles/' . $article->id, [], $headers)
->assertStatus(204);
}
public function testArticlesAreListedCorrectly()
{
factory(Article::class)->create([
'title' => 'First Article',
'body' => 'First Body'
]);
factory(Article::class)->create([
'title' => 'Second Article',
'body' => 'Second Body'
]);
$user = factory(User::class)->create();
$token = $user->generateToken();
$headers = ['Authorization' => "Bearer $token"];
$response = $this->json('GET', '/api/articles', [], $headers)
->assertStatus(200)
->assertJson([
[ 'title' => 'First Article', 'body' => 'First Body' ],
[ 'title' => 'Second Article', 'body' => 'Second Body' ]
])
->assertJsonStructure([
'*' => ['id', 'body', 'title', 'created_at', 'updated_at'],
]);
}
}
要運行測試的話的隨時運行 composer test 即可。
至此,我們已經完成了 API 接口的編寫和測試,下一篇我們會基于 JWT 對 API 進行認證同時整合進 Vue SPA 做一個更偏向實戰的教程,敬請期待。
總結
以上是生活随笔為你收集整理的larvel 中的api.php_Laravel API 系列教程(一): 基于 Laravel 5.5 构建 测试 RESTful API...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 项目编译失败_Swift源码编译让底层更
- 下一篇: php 上传文件漏洞,【文件上传】PHP