لاراول کار نوشتن api را برای ما تا جای ممکن ساده کرده است. در این مطلب توضیح داده‌ام که چطور برای نرم افزار خودمان یک api بسازیم و چطور دسترسی api را با استفاده از توکن محدود کنیم.

برای چه چیزی قرار است API بسازیم؟

یک نرم افزار ساده درست می‌کنیم که در آن یک سری کاربر در داخل دیتابیس داریم. برای نمایش و حذف کردن این کاربرها یک api درست می‌کنیم تا افراد از بیرون این نرم افزار بتوانند به این api دسترسی داشته باشند. کاربرها را بخوانند و یا یک کاربر را حذف کنند.

نصب لاراول و ساختن جدول در دیتابیس

پس اول از همه دستور زیر را اجرا کنید تا یک برنامه لاراولی ساخته شود.

composer  create-project laravel/laravel example-api

بعد باید یک دیتابیس ایجاد کنیم. من اسم این دیتابیس را گذاشتم example-api و مشخصات آن را در فایل env. وارد کردم.

یک جدول هم به نام users می‌سازیم تا اطلاعات کاربرها داخل آن قرار بگیرد.

لاراول به صورت پیشفرض migration برای جدول users دارد. کافی است با دستور artisan migrate این جدول را ایجاد کنیم.

php artisan migrate

وارد کردن کاربر فیک در دیتابیس

بعد از این باید یک سری کاربر وارد جدول کنیم. با کتابخانه faker لاراول، 20 تا کاربر برای جدول users می‌سازیم. با استفاده از seeder و factory در لاراول، این کار را انجام خواهیم داد.

با استفاده از factory یک کلاس درست می کنیم که به صورت اتوماتیک این کاربرها ساخته شوند.
پوشه databae/factories کلاس UserFactory به صورت پیش فرض وجود دارد. داخل آن یک متد به نام definition هست که مشخصات کاربر به صورت اتوماتیک با استفاده از کتابخانه faker ساخته می شود.


    public function definition()
    {
        return [
            'name' => $this->faker->name(),
            'email' => $this->faker->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
            'remember_token' => Str::random(10),
        ];
    }

برای اینکه تعداد زیادی کاربر بسازیم باید یک seeder ایجاد کنیم. این seeder تابع definition از کلاس UserFactory را صدا می‌زند و به تعدادی که ما تعیین می‌کنیم کاربر جدید می‌سازد.

دستور ساخت seeder:

php artisan make:seeder UserSeeder

با اجرای این دستور یک فایل به نام UserSeeder.php در پوشه seeders درست می شود. داخل این فایل یک متد به نام run هست. داخل این متد این کدها را می نویسیم:

<?php

namespace Database\Seeders;

use App\Models\User;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
  
    public function run()
    {
        User::factory(20)->create();
    }
}

در نهایت با اجرای دستور زیر کاربرها در دیتابیس وارد می‌شوند:

php artisan db:seed --class=UserSeeder

حالا جدول ما دارای 20 عدد کاربر است.

ساخت کنترلر برای User

در مرحله بعد برای هر کدام از نمایش و حذف کاربرها یک کنترلر ایجاد می‌کنیم. معمولا بهتر است کنترلرهای api را در یک پوشه جداگانه بسازیم. پس به این شکل یک کنترلر برای user خواهیم ساخت.

php artisan make:controller API/UserController

کنترلر ما در یک پوشه به نام API ساخته خواهد شد.

داخل این کنترلر برای هر کدام از عملیات‌هایی که قرار است انجام بدهیم باید یک متد داشته باشیم. منظورم این ها هستند:

-خواندن لیست کاربرها

-خواندن هرکدام از کاربرها

-حذف کاربر

تنها تفاوت با کنترلرهای معمولی در این است که مقادیر بازگشتی به صورت json خواهد بود.

ساخت route برای api

الان باید route تعریف کنیم تا ریکوئست به آنها زده شود و پاسخ به صورت json برگشت داده شود.

روت ها را در مسیر routes/api.php می‌نویسیم که از روت های اصلی نرم افزار جدا باشند.

استفاده از Route::apiResource() تمام روت‌های استاندارد برای یک api را در خود جای می‌دهد و نیازی به نوشتن تک تک آنها نیست. چون متدهایی که تعریف کردیم همگی با اسامی استاندارد laravel بودند. اگر از اسامی دلخواه برای متدها استفاده کنیم باید تک تک روت ها را خودمان تعریف کنیم.

use App\Http\Controllers\API\UserController;
use Illuminate\Support\Facades\Route;
Route::apiResource('users',UserController::class);

برای این که مطمئن شوید همه چیز درست بوده دستور php artisan route:list را اجرا کنید تا همه روت‌های نرم افزار را یک جا ببینید. با اجرای این دستور مشخص نی‌شود که برای هر کدام از متدهای UserController یک روت تعریف شده. متدها هم برای هر روت نوشته.

الان ما این روت ها را در اختیار داریم:

/api/users
api/users/{user}

وقتی روت ها را در فایل api.php می نویسیم:

  • در همه روت ها پیشوند api به طور خودکار اضافه می شود.
  • حفاظت csrf از روت ها دیگر انجام نمی شود.

روت ها ساخته شدند. یک کنترار هم برای مدیریت روت ها داریم. حالا باید متدهای این کنترلر را بنویسیم. همانطور که در عکس قبل دیدید برای کاربرها به 5 متد نیاز داریم

  • index
  • show
  • destroy
  • store
  • update

اما من قصد ندارم هر 5 تای اینها را پیاده سازی کنم. فعلا به سه تای اینها می پردازم تا با نحوه کارکرد api آشنا شویم.

یک متد برای نمایش همه کاربران (index) یک متد برای نمایش هر کاربر با استفاده از email و یک متد هم برای حذف کاربر(destroy).

پیاده سازی متدهای UserController

پس فایل UserController این شکلی خواهد شد:

<?php

namespace App\Http\Controllers\API;

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

class UserController extends Controller
{
    public function index(){

    }

    public function show(){

    }

    public function destroy(){
        
    }
}

تعریف تابع index:

این تابع همه کاربرهای موجود در جدول users را بر می‌گرداند. این را می‌دانیم که در api ما باید پاسخ را به صورت json برگردانیم. پس متد به این شکل خواهد بود:

use App\Models\User;

 public function index(){

        $users = User::all();

        return $users;


    }

مقدار users$ اینجا به صورت پیشفرض json است برای همین کافی است آن را برگردانیم.

قبل از ادامه کار بیایید api را تست کنیم. url مرتبط با متد index مقدار api/users بود. برای تست آن از نرم افزار Postman استفاده می‌کنیم.

postman را اجرا می‌کنیم. متد ارسال را روی get تنظیم و url مورد نظر را هم می‌نویسیم.

با ارسال ریکوئست لیست کاربرها به صورت json باید نمایش داده شوند.

این url چون متد GET داشت در مرورگر هم قابل نمایش است.

تعریف متد show:

با این متد قصد ما این است که فقط یک کاربر را برگردانیم. یعنی یک مقدار مثلا id یا ایمیل را دریافت کنیم و بعد اطلاعات آن را نمایش دهیم. یا اصلا چک کنیم این کاربر وجود دارد یا خیر. در برنامه های واقعی از ایمیل استفاده می‌کنند. اینجا ما هم همین کار را می‌کنیم.

    public function show($email)
    {

        $user = User::where('email', $email)->first();

        return $user;
    }

اگر یادتان باشد روت این متد به این شکل بود:

api/users/{user}

به جای عبارت {user} باید id یا email یا هر مشخصه ای که خودتان در نظر دارید قرار دهید و بعد طبق آن کوئری بزنید و کاربر را پیدا کنید. من اینجا با استفاده از ایمیل این کاربر را دریافت کردم. البته بهتر بود که قبل از کوئری زدن ایمیل را اعتبارسنجی می‌کردیم.

بیایید این یکی را هم تست کنیم:

به جای {user} ایمیل یکی از کاربرهایی که در دیتابیس بود را نوشتم. دیدید که اطلاعات این کاربر را برگرداند.

تعریف متد destroy:

کار این متد هم حذف کاربر است.


    public function destroy($email)
    {
        $user = User::where('email', $email)->first();

        $user->delete();

        return json_encode(['message'=>'User Removed Successfully']);

    }

در این متد اول با استفاده از email کاربر را پیدا می‌کنیم. بعد با متد delete کاربر را حذف می‌کنیم. برای اینکه به کسی که ریکوئست را ارسال کرده اطمینان بدهیم که عملیات حذف درست انجام شده، یک پاسخ به صورت json برمی‌گردانیم.

و اما تست این متد:

این بار هم به جای {user} ایمیل را می‌نویسیم فقط باید متد را DELETE انتخاب کنیم.

url این متد هم مانند متد show است تنها تفاوت انها در متد ارسالی آن هاست. برای استفاده از متد destroy ریکوئست باید به شکل DELETE ارسال شود.

محدود کردن دسترسی به API

تا اینجا api ما ساخته شد اما یک اشکال بزرگ دارد. هر کسی از هر جایی قادر است به این api دسترسی پیدا کند. لیست کاربرها را ببیند و یا یک کاربر را حذف کند. بیایید این مشکل را حل کنیم.

برای این راه حل های مختلفی هست مثل استفاده از jwt یا صدور توکن معمولی. من اینجا با استفاده از صدور توکن این کار را انجام می‌دهم.

برای این کار، لاراول یک پکیج بسیار خوب و ساده دارد به نام laravel sanctum. ما با این پکیج برای API ای که ساختیم توکن ایجاد می‌کنیم و کابران مورد نظر ما با استفاده از این توکن می توانند از api استفاده کنند.

نصب پکیج Sanctum

اگر از آخرین نسخه لاراول استفاده می‌کنید باید laravel sanctum روی آن نصب باشد در غیر ان صورت با دستور زیر آن را نصب کنید:

composer require laravel/sanctum

بعد با دستور vendor:publish باید فایل های config و migration پکیج را ایجاد کنید

php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

در آخر هم باید جدول دیتابیس را بسازید:

php artisan migrate

جدولی خواهید داشت به نام personal_access_tokens که توکن های ایجاد شده در آن ذخیره می‌شوند.

صدور توکن برای api

یک نکته را در نظر داشته باشید که توکن هایی که اینجا صادر می‌شود باید حتما از طریق header درخواست(request) ارسال شود.

اول از همه مدل User باید تریت(tarit) Laravel\Sanctum\HasApiTokens را استفاده کند.

use Laravel\Sanctum\HasApiTokens;
 
class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}

یک روت جدید درست می‌کنیم تا یک صفحه داشته باشیم برای نمایش و ایجاد توکن.

Route::get('/token-form',function (){

    return view('token-form');

});

یک فایل view هم می‌سازیم برای نمایش فرم:

<form action="/generate-token" method="post">
        @csrf
        <label for="">Token Name</label>
        <input name="token_name" type="text" placeholder="choose a name for your token" >
        <input type="submit" value="Generate Token">

    </form>

یک فرم که کاربر با کلیک روی آن می تواند یک توکن ایجاد کند.

برای اینکه کاربری توکن ایجاد کند قبلش باید لاگین کرده باشد. اینجا برای اینکه مثال را نمایش دهم یک روت برای لاگین درست می‌کنم و با این کد به صورت دستی یکی از کاربرها را لاگین میکنم:

Route::get('/login',function(){
    Auth::loginUsingId(1);
});

یک روت دیگر هم برای انجام عملیات ایجاد توکن و تحویل آن به کاربر.

Route::post('/generate-token',function(Request $request){

    $token = $request->user()->createToken($request->token_name);
    return ['token'=> $token->plainTextToken];
});

بعد از اینکه فرم ارسال شد، متد createToken صدا زده و یک توکن صادر می‌شود که به شکل آبجکت در متغیر token$ ذخیره شده. بعد از این به صورت رمزگذاری شده در دیتابیس ذخیره خواهد شد. ولی برای نمایش به کاربر هم با استفاده از متد plainTextToken این کار امکان پذیر است.

اعتبار سنجی ریکوئست ها به API با توکن

الان که توکن ساخته شد، باید کاری کنیم تا api ما بدون استفاده از این توکن برای کاربر اجرا نشود.

برای این کار باید از middleware پکیج sanctum روی روت های مورد نظرتان در api.php استفاده کنید. پس در فایل api.php این middleware را اضافه می‌کنیم.

Route::middleware(['auth:sanctum'])->apiResource('users',UserController::class);

حالا اگر این api را دوباره با postman تست کنید ارور خواهد داد. البته وقتی ریکوئست ارسال می کنید باید حتما

در header مقدار Accept را برابر application/json قرار دهید.

برای انجام احراز هویت باید توکن را داخل هدر Authorization قرار دهیم تا api درست به نمایش در بیاید. به این شکل:

بعد از اینکه token را در header نوشتیم حالا دوباره ریکوئست را ارسال می‌کنیم. این بار با انجام احراز هویت نتایج api نمایش داده می شوند.

جمع بندی

برای ساخت api در لاراول ابتدا روت های مورد نظرمان را در routes/api.php ایجاد کردیم. بعد برای روت‌ها کنترلر نوشتیم تا اطلاعات را در داخل کنترلر پردازش کنیم و به کاربر تحویل دهیم.

چون معمولا نیاز داریم که دسترسی api ها را محدود کنیم، پکیج laravel Sanctum را برای ایجاد توکن ها و محدود کردن دسترسی نصب کردیم. Sanctum خیلی ساده امکانات ساخت توکن دسترسی و محدود سازی api را در اختیارمان می گذارد.