從零開始學會如何用Laravel寫出Restful API

從零開始學會如何用Laravel寫出Restful API

術語說明

在開始實作 API 之前,了解一些常見的術語是很有必要的

什麼是RESTful

REST,全名 Representational State Transfer( 表現層狀態轉移),它是一種設計模式,RESTful 只是從名詞轉為形容詞,像是 peace 轉成形容詞是 peaceful。RESTful 則形容以此規範設計的 API,稱為 RESTful API。換言之,符合 REST 規範的 API,就被稱為 RESTful API

RESTful API 一般來說需要符合以下的規範

前後端分離

將前端和後端分離開來,而後端的 API 端口在標準設計上面須按照 RESTful 風格規範,目的在於使其能夠讓多種前端的平台串接,比如其他網頁程式又或者是iOS.Android App,以提高可擴充性

無狀態

每一次的 API 請求都是獨立的,從前端發出的請求須包含所有必要資訊,並且請求之間不會互相影響,意味著無法利用 Session。所有狀態的保存都必須由前端來自行完成

可快取(Cacheable)

後端需明確定義該請求的回應是否為可快取的(Cachable),若為可快取,當取得回應之後,下次從前端發相同的請求(網址和參數都相同)到後端時,前端就可以安心的重複使用之前所得到的回應,省下再做一次請求的時間

網址不可有動詞存在

每個網址一般都與某一個資源 (resource) 有關,也就是表格的小寫名稱 ( 如: posts、books、products 等,為了把網址設計得更簡潔,透過標準的網址設計規範,就能夠說明其用處,而不需要再加上動詞

比如 /posts/1 ,表示其動詞類似 show ,是顯示主鍵為1的 posts 資料

另外,URI 的名詞最好用複數型,避免使用大寫。如此可確保網址的格式一致,可讀性也較高

每個 '/' 字元代表的是層級關係,而網址結尾最好不要加上 '/'

什麼是API

API 是 Application Programming Interface 的簡稱,也就是應用程式介面,關於更多細節請參考下面的影片

看完影片,能回答什麼是 API 了嗎?

從剛才的影片可以知道,API就是系統之間溝通所使用的橋樑。以餐廳的例子來看,客人只看得懂 Menu ,廚房只看得懂菜色的代碼,而餐廳服務生所扮演的 API 就需要協助把客人所點的菜色轉換成廚房理解的代碼,讓兩個不同的系統可以互相溝通

什麼是API端口(Endpoint)

你已經知道 API 是前後端之間的溝通窗口,但更精確的說,若前端要串接 API 時,其實是透過 API 的端口來取得資源

以下列出了一般 API 與 RESTful API 的端口差異給你參考

一般 API
目的 方法 URI
取得所有文章資料 GET /getPosts
新增文章 POST /createPost
取得某個文章資料 GET /getPost/{id}
更改某個文章資料 POST /updatePost/{id}
刪除某個文章 POST /deletePost/{id}
Restful API
目的 方法 URI
取得所有文章資料 GET /posts
新增文章 POST /posts
取得某個文章資料 GET /posts/{id}
更改某個文章資料 PUT/PATCH /posts/{id}
刪除某個文章 DELETE /posts/{id}

後端工程師在開發 API 時實際上就是在依據 API 文件在開發端口

快速開始

此次實作示範的表格結構 此次實作示範的表格結構

Step 1.生成 posts 表格

建立 posts 表格的 Migration 檔案

php artisan make:migration create_posts_table

編輯 Migration 內容如下:

//database\migrations\xxxx_xx_xx_xxxxxx_create_posts_table.php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
       Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title', 50);
            $table->text('content');
            $table->string('status', 10)->default('draft');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}

執行 migrate 來生成 posts 表格

php artisan migrate

生成 Post.php 作為 Eloquent 模型

php artisan make:model Post

Step 2.建立假資料

建立 假資料工廠(Factory)

建立 PostFactory.php 檔案,該檔案用來定義生成的欄位資料

php artisan make:factory PostFactory

編輯 PostFactory.php 的內容如下:

//database\factories\PostFactory.php

namespace Database\Factories;

use App\Models\Post;
use Illuminate\Database\Eloquent\Factories\Factory;

class PostFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = Post::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        return [
            'title' => $this->faker->name,
            'content' => $this->faker->realText(50),
            'status' => $this->faker->randomElement(array('published', 'draft'))
        ];
    }
}
用 Seeder生成假資料

修改 DatabaseSeeder.php,內容如下:

//database\Seeder\DatabaseSeeder.php

public function run()
{
    \App\Models\User::truncate();
    \App\Models\User::factory(10)->create();
    \App\Models\Post::truncate();
    \App\Models\Post::factory(10)->create();
}

開啟 Terminal ,輸入以下指令來生成假資料

php artisan db:seed

Step 3.新增&編輯控制器

新增 PostController.php 用來處理 posts 表格的 API

輸入以下指令來新增 PostController.php,加上選項 api 內容會自帶幾個常見方法,如 index() . store() . show() 等等

php artisan make:controller Api/PostController --api

編輯 PostController.php 內容如下:

//app\Http\Controllers\Api\PostController.php

namespace App\Http\Controllers\Api;

use Illuminate\Http\Request;
use App\Models\Post;

class PostController extends Controller
{
    //用於生成 JSON 字串
    private function makeJson($status, $data, $msg)
    {
        //轉 JSON 時確保中文不會變成 Unicode
        return response()->json(['status' => $status, 'data' => $data, 'message' => $msg])->setEncodingOptions(JSON_UNESCAPED_UNICODE);
    }    

    public function index()
    {
        $posts = Post::get();

        if(isset($posts) && count($posts) > 0){
            $data = ['posts' => $posts];
            return $this->makeJson(1,$data,null);
        }else{
            return $this->makeJson(0,null,'找不到任何文章');
        }

    }

    public function show(Request $request,$id)
    {
        $post = Post::find($id);

        if(isset($post)){
            $data = ['post' => $post];
            return $this->makeJson(1,$data,null);
        }else{
            return $this->makeJson(0,null,'找不到該文章');
        }

    }

    public function store(Request $request)
    {
        $input = ['title' => $request->title , 'content' => $request->content];

        $post = Post::create($input);

        if(isset($post)){
            $data = ['post' => $post];
            return $this->makeJson(1,$data,'新增文章成功');
        }else{
            $data = ['post' => $post];
            return $this->makeJson(0,null,'新增文章失敗');
        }

    }

    public function update(Request $request,$id)
    {

        try {
            $post = Post::findOrFail($id);
            $post->title = $request->title;
            $post->content = $request->content;
            $post->save();
        } catch (Throwable $e) {
            //更新失敗
            $data = ['post' => $post];
            return $this->makeJson(0,null,'更新文章失敗');
        }

        $data = ['post' => $post];
        return $this->makeJson(1,$data,'更新文章成功');
    }

    public function destroy($id)
    {
        try {
            $post = Post::findOrFail($id);
            $post->delete();
        } catch (Throwable $e) {
            //刪除失敗
            return $this->makeJson(0,null,'刪除文章失敗');
        }
        return $this->makeJson(1,null,'刪除文章成功');
    }

}

Step 4.撰寫路由

因為 API 所需要的安全性與中介層和網頁請求不同,因此它的路由會被寫在 api.php

web與api各自所使用的中介層及路由前綴,請見app\Providers\RouteServiceProvider.php

在 routes/api.php 加入對應的 API 路由

//routes\api.php

Route::middleware('auth:api')->group(function () {
    Route::apiResource('posts', 'App\Http\Controllers\Api\PostController');
});

Step 5.建立 API 驗證機制

這邊我們可以透過 JWT 來建立 API 驗證機制,詳細步驟請參考15分鐘學會如何實作API的驗證機制

Step 6.測試

最後可使用 PostMan來進行測試,為方便說明直接以影片來進行說明


參考資料


分享這篇文章:

關聯文章: