李炎恢 Laravel API介面學習筆記[基於 Laravel Sail 環境]

Alcohol發表於2021-07-02

0. 學前準備:

1. 前言

這是一節短小精悍的 Laravel API 開發入門課,很基礎,但對我來說也很有用。
這個課程在時間順序上是在《李炎恢 / Laravel / 核心篇 / PHP框架 / 階段一》課程之後的,所以如果你跟我一樣只看這門課程的話,可能會遇到沒有資料庫資料的問題,但是這個問題容易解決,用 Laravel 自帶的資料填充功能填充一下就搞定。
另外,與視訊教程相比,我增加了兩點內容:

  • 教程裡面沒有編寫update方法,我按照自己的理解補充了這部分內容,如果大家有更好的寫法,不妨在評論區 show you code
  • 我按照視訊裡面的編碼,發現儲存到資料庫裡面的使用者密碼是明文儲存,沒有加密,所以我改動了這部分內容
    建議大家看一看原視訊哦。
    還有,想知道如何使用 Laravel Sail 環境請參考:歷時三天,成功搭建Laravel Sail環境

2. 建立專案

curl -s https://laravel.build/yanhui | bash

如上所示,我建立一個名為 yanhui 的 Laravel 專案。
在 Terminal 中啟動專案:

./vendor/bin/sail up

如圖所示:

此時,我用瀏覽器開啟 http://localhost/ ,就可以看到以下畫面,說明專案已經成功建立並且啟動(這個畫面我們不需要更改):

3. 建立用於 API 操作的資源控制器和對應路由

先在 Terminal 終端配置一下 Sail 命令,讓它變得簡短一點:

alias sail='bash vendor/bin/sail'

然後,使用以下命令建立 API 控制器

sail artisan make:controller UserController --api

--api 引數的意義是,從控制器中排除 createedit 方法。
此處出現在我終端裡面的 爆紅 提示,是因為我在之前學習的時候已經建立過同名控制器,所以我再次演示就會這個提示,大家無視就好。
最後,我輸入 code .,使用 VS code 開啟專案資料夾。
整個流程如下圖所示:

至此,控制器已經建立好,接下來是對應路由,在建立路由之前,我們我們可以使用 sail artisan route:list 命令來檢視一下,當前專案的路由列表是怎樣的(如果你是新建的專案,應該會看到如下所示的畫面):

接下來,我們開啟 routes\api.php 檔案,用一下程式碼,建立資源路由(檔案原本的程式碼我們先不改動,注意頂部引入控制器):

<?php
use App\Http\Controllers\UserController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| 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::apiResource('user',UserController::class);

可以看到,與原檔案相比,我只新增了兩句程式碼:

use App\Http\Controllers\UserController;
Route::apiResource('user',UserController::class);

此時,我們再次在終端中輸入 sail artisan route:list 命令,可以看到如下畫面:
然後,我們來測試一下:

app\Http\Controllers\UserControllers.php 中,我們修改 index 方法:

public function index()
    {
        return '你好,Laravel~';
    }

然後我們用瀏覽器開啟 http://localhost/api/user
可以看到如下畫面:

最後,我們用專業的介面除錯軟體 Postman 開啟http://localhost/api/user
可以看到以下畫面:

至此,控制器和路由建立並測試成功。

4. 建立一個專門用於生成 API 的控制類,然後繼承並呼叫它

在終端中使用命令建立控制器:

sail artisan make:controller ApiController

在生成的控制器中,我們編寫一個 create 方法:(檔案路徑:app\Http\Controllers\ApiController.php)

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ApiController extends Controller
{
    protected function create($msg = '', $code = 200, $data = [])
    {
        $result = [
            // 返回狀態碼
            'code'=>$code,
            // 返回提示資訊
            'msg'=>$msg,
            // 返回資料
            'data'=>$data,
        ];
        return response($result);
    }
}

然後,我們修改 UserController.php 檔案(檔案路徑:app\Http\Controllers\UserController.php)
使其繼承 ApiController 而不是原本的 Controller,這樣我們就可以很自然地呼叫 create 方法,來生成標準的 API 返回資訊。
詳情如下:

class UserController extends ApiController
{
    public function index()
    {
        return $this->create('請求成功',200,'你好,Laravel~');
    }

此時,我們用Postman 再次傳送請求,可以看到如下畫面,說明控制類建立成功。

5. 資料列表和分頁

填充使用者資料

在本小節,我們將真正編寫第一個控制器方法的具體邏輯,但是,對於沒有看前置課程的同學來說,我們也將面臨一個問題:我們的資料庫裡面,沒有任何資料
好在,這個問題我們可以用 Laravel 的資料填充功能來解決,至於填充的原理,我就不再贅述,只講怎麼做,想進一步瞭解原理的同學可以閱讀 Laravel 文件,或者參考本社群 《L01 Laravel 教程》的第 8 章第 4 小節。
資料填充第一步:建立 seeder 類

sail artisan make:seeder UserSeeder

生成的 UserSeeder 檔案位於:database\seeders\UserSeeder.php
讓我們編輯其中的 run 方法,如下所示:

<?php
namespace Database\Seeders;
use App\Models\User;
use Illuminate\Database\Seeder;
class UserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        User::factory(50)->create();
    }
}

User::factory(50)->create(); 這句程式碼會呼叫使用者工廠方法,為我們生成 50 條使用者資料。
使用者工廠,Laravel 預設自帶,檔案位於 database\factories\UserFactory.php,保持預設即可,暫時不需要做修改。
然後我們修改 DatabaseSeeder.php 檔案中的 run 方法,讓其呼叫我們建立的 UserSeeder 類:
檔案位置:database\seeders\DatabaseSeeder.php

<?php
namespace Database\Seeders;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        Model::unguard();
        $this->call(UserSeeder::class);
        Model::reguard();
    }
}

現在,讓我們回到終端中,使用以下命令,生成 Laravel 預設的使用者資料表:

sail artisan migrate

接著用以下命令,填充資料

sail artisan db:seed

終端畫面如下所示:

至此,資料填充完成,如果你用 Navicat 等工具連線並開啟資料庫中的 users 表,就可以看到 Laravel 為我們生成的 50 條使用者資料,這裡就不演示了。

編寫 UserController 控制器的 index 方法

資料庫有了資料,我們接下來編寫的控制器方法才有意義,首先我們來看第一個控制器方法:
檔案位置:app\Http\Controllers\UserController.php

public function index()
    {
        // 資料獲取
        // 返回資料

        $data = User::select('id', 'name', 'email')->simplePaginate(10); // 簡單分頁
        return $this->create('資料獲取成功', 200, $data);
    }

可以看到,我通過查詢構造器,從 users 表的每條記錄中選出了三個欄位,並把所有的記錄進行簡單分頁,每頁展示 10 條資料。
用 Postman 進行除錯,可以看到如下畫面:

可以看到,之前填充的資料,被我們從資料庫調出來。
index 方法還有一些其他的寫法,這裡僅做記錄,有興趣的同學可以自己除錯以下寫法:

// 獲取全部可顯示欄位
$data = User::get();
// 獲取指定欄位 
$data = User::select('id','name','email')->get();
// 獲取指定欄位並分頁
$data = User::select('id','name','email')->paginate(10);

至此,查詢所有資料並且分頁的方法,編寫完成。

6. 配置一個 404 的 HTTP 異常,並且用 JSON 格式返回(覆蓋系統自帶的 404 提示)

Larave 框架自帶 404 提示,為什麼我們還有重新寫一個呢?
因為自帶的 404 提示不符合 API 格式,這你讓前端很難做事的,所以,我們要重寫一個符合 API 格式的 404 提示,覆蓋原有的提示。
操作起來也簡單,兩步走:

  1. 在 resources/views 目錄下建立 errors 資料夾
  2. 在 resources/views/errors 目錄下建立 404.blade.php 檔案,並編寫返回的內容

你可以手動建立相關目錄和檔案,也可以像我一樣,在終端中使用命令來建立:

mkdir resources/views/errors && touch resources/views/errors/404.blade.php

同樣,因為我在之前已經建立過,所以,此時演示的時候,提示資料夾已存在。

接下來我們開始編寫 resources/views/errors 目錄下的 404.blade.php 檔案,很簡單,以下就是檔案的全部內容:

<?php
$result = [
    // 返回狀態碼
    'code' => 404,
    // 返回提示資訊
    'msg' => '資源不存在',
    // 返回資料
    'data' => [],
];
echo json_encode($result);

接下來我們在 Postman 裡面除錯一下,填入一個不存在的路徑,看看是不是我們自己編寫的 404 提示(如果各位的資訊顯示有點亂的話,可以注意我標記紅框的位置):

7. 展示單個資料 API

展示單個資料,對應的是控制器的 show 方法,通過傳入的 $id 來返回對應的使用者資訊。
但是根據 web 開發工程師第一準則:永遠不要相信使用者輸入的資料
所以我們要考慮更多的東西,比如:

  • 我們要求使用者傳入的引數是數字,但是使用者扔給我們一個單詞,我們該怎麼辦?
  • 現在我們資料庫裡面數值的範圍在 1 - 50 之內,使用者給我們拋個 100 過來,超出了我們的資料範圍,我們又該返回什麼樣的資訊呢?

所以,show 方法編寫如下,具體的思路請看註釋部分文字:
檔案位置: app\Http\Controllers\UserControllers.php

public function show($id)
    {
        // 獲取資料
        // 資料存在 返回成功資訊
        // 資料不存在
        // 引數超出範圍導致資料不存在
        // 引數型別錯誤導致資料不存在

        $data = User::select('id', 'name', 'email')->find($id);

        if (isset($data)) {
            return $this->create('資料獲取成功', 200, $data);
        } else {
            if (is_numeric($id)) {
                return $this->create('使用者不存在', 204, []);
            } else {
                return $this->create('id 引數錯誤', 400, []);
            }
        }
    }

Postman 除錯如下(正確引數):

非數字引數:

超範圍引數:

8. 新增資料 API

新增資料對應的控制器方法,是 store 方法,這部分程式碼我寫的與原視訊裡面有些不同,主要區別就是在講資料存入資料庫的時候,我將密碼用 md5() 函式進行加密,雖然這種方式也比較樸素,但是,起碼資料庫裡面的資料不再是赤裸裸的 123456 了,這是什麼?這就是進步啊~
檔案位置: app\Http\Controllers\UserControllers.php
編碼如下:

public function store(Request $request)
    {
        // 接收資料
        $data = $request->all();
        // 資料驗證
        $validator = Validator::make($data, [
            'name' => 'required|min:2|max:50',
            'email' => 'required|min:2|max:50|email',
            'password' => 'required|min:6|max:50',
        ]);
        if ($validator->fails()) {
            return $this->create($validator->errors(), 400);
        } else {
            $newUser = User::create([
                'name' => $request->name,
                'email' => $request->email,
                'password' => md5($request->password),
            ]);
            $newUser = User::select('id', 'name', 'email')->find($newUser['id']);
            return $this->create('使用者建立成功', 200, $newUser);
        }
    }

關於資料驗證程式碼的解析請看原視訊,或者參考本社群 《L01 Laravel 教程》的第 8 章第 4 小節。或者參考本社群 《L01 Laravel 教程》的第 6 章,此處點到為止。
以下是 Postman 除錯環節,請注意我標紅框的部分,都是細節哦:

9. 刪除資料 API

有增加就有刪除,就連後端人員的自嘲——增刪改查工程師 的頭兩個字,也是
刪除資料對應的控制器方法,是 destroy 方法,其邏輯與增加資料很相似。
檔案位置: app\Http\Controllers\UserControllers.php
編碼如下:

public function destroy($id)
    {
        // 獲取資料
        // 資料存在,刪除
        // 資料不存在
        // 引數超出範圍導致資料不存在
        // 引數型別錯誤導致資料不存在
        $data = User::find($id);
        if (isset($data)) {
            if ($data->delete()) {
                return $this->create('使用者刪除成功', 200);
            }
        } else {
            if (is_numeric($id)) {
                return $this->create('使用者不存在,無法刪除', 204, []);
            } else {
                return $this->create('id 引數錯誤', 400, []);
            }
        }
    }

但是,要注意一點,在實際的生產環境中,無論我們多麼想刪除資料庫裡面的資料,事實上我們都不會刪,因為我們受過專業的訓練(毒打)。
那麼,如何讓存在資料裡面的資料,看起來像是“被刪除”的樣子呢?
答案是增加一個 狀態欄位,如果一條資料的狀態欄位的值是 1,代表這是一條正常的記錄;如果是 -1 或者其它值,代表這是一條已經被刪除的記錄,沒有許可權的人是查不到這條記錄的。
由此,引申出web工程師第二準則:永遠不要刪除生產環境的資料。(除非你想搞破壞)
接下來是 Postman 除錯(就刪除我們剛剛建立的第 51 號使用者好了):

此時如果我們開啟資料庫,發現第 51 條記錄已經沒有了,再次提示,這只是在本地開發環境的學習過程,在生產環境中,任何與刪除有關的動作都是很危險的,一定要慎之又慎。

10. 修改資料 API

在原視訊中,是沒有這部分內容的,但是,出來混,要講信用,說了增刪改查工程師,就要增刪改查,一個都不能少。
以下是我通過自己的理解寫出來的,還是那就說話,如果有更好的寫法,請在評論區 show you code
檔案位置: app\Http\Controllers\UserControllers.php
編碼如下:

public function update(Request $request, $id)
    {
        // 接收資料
        // 驗證資料
        // 查詢使用者
        // 如果使用者不存在
        // 更新並返回資訊
        $data = $request->all();
        $validator = Validator::make($data, [
            'name' => 'required|min:2|max:50',
            'password' => 'required|min:6|max:50',
        ]);
        if ($validator->fails()) {
            return $this->create($validator->errors(), 400);
        }
        $user = User::find($id);
        if (isset($user)) {
            if ($user->update([
                'name' => $request->name,
                'password' => md5($request->password),
            ])) {
                return $this->create('使用者更新成功', 200);
            }
        } else {
            if (is_numeric($id)) {
                return $this->create('使用者不存在,無法更新', 204, []);
            } else {
                return $this->create('id 引數錯誤', 400, []);
            }
        }
    }

程式碼思路請看註釋文字,以下是 Postman 除錯時間:
查詢第一個使用者的資訊如下:

然後呼叫api,修改 1 號使用者的 name 和 password 資訊:

最後,再次檢視,可以看到,已經修改成功:

至此,整個課程筆記已經結束。
因為之前的程式碼是分塊編寫並解釋,觀感並不是很好,所以在附錄部分,貼上 app\Http\Controllers\UserControllers.php 的完整程式碼,供大家參考,以免出錯。

附錄

完整的app\Http\Controllers\UserControllers.php 如下:

<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Auth\Events\Validated;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class UserController extends ApiController
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        // 資料獲取
        // 返回資料

        $data = User::select('id', 'name', 'email')->simplePaginate(10); // 簡單分頁
        return $this->create('資料獲取成功', 200, $data);

        // 資料獲取的其它寫法舉例
        // 獲取全部可顯示欄位
        // $data = User::get();
        // 獲取指定欄位 
        // $data = User::select('id','name','email')->get();
        // 獲取指定欄位並分頁
        // $data = User::select('id','name','email')->paginate(10);
    }
    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        // 接收資料
        $data = $request->all();
        // 資料驗證
        $validator = Validator::make($data, [
            'name' => 'required|min:2|max:50',
            'email' => 'required|min:2|max:50|email',
            'password' => 'required|min:6|max:50',
        ]);
        if ($validator->fails()) {
            return $this->create($validator->errors(), 400);
        } else {
            $newUser = User::create([
                'name' => $request->name,
                'email' => $request->email,
                'password' => md5($request->password),
            ]);
            $newUser = User::select('id', 'name', 'email')->find($newUser['id']);
            return $this->create('使用者建立成功', 200, $newUser);
        }
    }
    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        // 獲取資料
        // 資料存在 返回成功資訊
        // 資料不存在
        // 引數超出範圍導致資料不存在
        // 引數型別錯誤導致資料不存在

        $data = User::select('id', 'name', 'email')->find($id);
        if (isset($data)) {
            return $this->create('資料獲取成功', 200, $data);
        } else {
            if (is_numeric($id)) {
                return $this->create('使用者不存在', 204, []);
            } else {
                return $this->create('id 引數錯誤', 400, []);
            }
        }
    }
    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        // 接收資料
        // 驗證資料
        // 查詢使用者
        // 如果使用者不存在
        // 更新並返回資訊
        $data = $request->all();
        $validator = Validator::make($data, [
            'name' => 'required|min:2|max:50',
            'password' => 'required|min:6|max:50',
        ]);
        if ($validator->fails()) {
            return $this->create($validator->errors(), 400);
        }
        $user = User::find($id);
        if (isset($user)) {
            if ($user->update([
                'name' => $request->name,
                'password' => md5($request->password),
            ])) {
                return $this->create('使用者更新成功', 200);
            }
        } else {
            if (is_numeric($id)) {
                return $this->create('使用者不存在,無法更新', 204, []);
            } else {
                return $this->create('id 引數錯誤', 400, []);
            }
        }
    }
    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        // 獲取資料
        // 資料存在,刪除
        // 資料不存在
        // 引數超出範圍導致資料不存在
        // 引數型別錯誤導致資料不存在
        $data = User::find($id);
        if (isset($data)) {
            if ($data->delete()) {
                return $this->create('使用者刪除成功', 200);
            }
        } else {
            if (is_numeric($id)) {
                return $this->create('使用者不存在,無法刪除', 204, []);
            } else {
                return $this->create('id 引數錯誤', 400, []);
            }
        }
    }
}

完結,撒花,感謝閱讀。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章