RAJESTARY DIGITAL TECH • SECURITY • EDUCATION
دانش • فناوری • امنیت

دنیای تکنولوژی
از یادگیری شروع می‌شود

آموزش‌های کاربردی، برنامه‌نویسی، امنیت و فناوری برای ساختن، یادگرفتن و بهتر زندگی کردن با تکنولوژی.

LEARN BUILD SECURE CREATE

تست و دیباگ API حرفه‌ای

تست و دیباگ API حرفه‌ای

📖 درس نهم: تست و دیباگ API حرفه‌ای

👋

سلام مهمان عزیز!

برای دسترسی به تمام محتوا و امکانات، وارد شوید.

🎯 هدف این درس: آشنایی با تست‌نویسی حرفه‌ای API با PHPUnit، Mocking، Logging پیشرفته، استفاده از Postman Collection و تکنیک‌های دیباگ و عیب‌یابی.

مهمان عزیز! 👋
تا اینجا یک API کامل با امنیت، احراز هویت، مستندات، کش و نسخه‌بندی ساخته‌ایم. اما چطور مطمئن شویم که API ما درست کار می‌کند؟

پاسخ: تست‌نویسی! در این درس با ابزارها و تکنیک‌های تست و دیباگ حرفه‌ای آشنا می‌شویم تا بتوانیم API خود را به صورت کامل تست کنیم و از صحت عملکرد آن اطمینان حاصل کنیم.

💡 چرا تست‌نویسی مهم است؟

  • ✅ اطمینان از صحت عملکرد API.
  • ✅ جلوگیری از بازگشت باگ‌ها (Regression).
  • ✅ مستندسازی خودکار رفتار API.
  • ✅ افزایش اعتماد به کد.
  • ✅ تسهیل همکاری تیمی.
  • ✅ کاهش هزینه‌های نگهداری.

🧪 نصب و راه‌اندازی PHPUnit

PHPUnit محبوب‌ترین فریم‌ورک تست‌نویسی در PHP است. برای نصب آن از Composer استفاده می‌کنیم:



📦
Install PHPUnit

Composer




# نصب PHPUnit به عنوان وابستگی توسعه
composer require --dev phpunit/phpunit

# اجرای تست‌ها
vendor/bin/phpunit

# فایل phpunit.xml (پیکربندی)
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="tests/bootstrap.php"
         colors="true"
         verbose="true"
         stopOnFailure="false">
    <testsuites>
        <testsuite name="API Tests">
            <directory>tests/Unit</directory>
            <directory>tests/Feature</directory>
        </testsuite>
    </testsuites>
    
    <coverage>
        <include>
            <directory>src</directory>
        </include>
    </coverage>
</phpunit>

🧪 نوشتن Unit Test برای Model

ابتدا تست‌های واحد (Unit Test) برای مدل User می‌نویسیم:



🧪
UserTest.php

PHP
8.2




<?php

use PHPUnit\Framework\TestCase;

class UserTest extends TestCase
{
    private $userModel;
    private $db;

    // قبل از هر تست، دیتابیس تست را آماده کن
    protected function setUp(): void
    {
        parent::setUp();
        
        // اتصال به دیتابیس تست (SQLite in-memory)
        $this->db = new PDO('sqlite::memory:');
        $this->db->exec("CREATE TABLE users (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            email TEXT NOT NULL UNIQUE,
            password TEXT NOT NULL,
            role TEXT DEFAULT 'user',
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP
        )");

        // تزریق دیتابیس به مدل (با Reflection)
        $this->userModel = new User();
        $reflection = new ReflectionClass($this->userModel);
        $property = $reflection->getProperty('db');
        $property->setAccessible(true);
        $property->setValue($this->userModel, $this->db);
    }

    // تست ایجاد کاربر
    public function testCreateUser()
    {
        $userData = [
            'name' => 'یونس',
            'email' => 'younes@example.com',
            'password' => 'password123',
            'role' => 'admin'
        ];

        $user = $this->userModel->create($userData);

        // Assertions
        $this->assertNotNull($user);
        $this->assertEquals('یونس', $user['name']);
        $this->assertEquals('younes@example.com', $user['email']);
        $this->assertEquals('admin', $user['role']);
        $this->assertArrayHasKey('id', $user);
    }

    // تست پیدا کردن کاربر
    public function testFindUser()
    {
        // ایجاد کاربر
        $userData = [
            'name' => 'مریم',
            'email' => 'maryam@example.com',
            'password' => 'password123'
        ];

        $created = $this->userModel->create($userData);
        
        // پیدا کردن کاربر
        $found = $this->userModel->find($created['id']);

        $this->assertNotNull($found);
        $this->assertEquals('مریم', $found['name']);
        $this->assertEquals('maryam@example.com', $found['email']);
    }

    // تست بروزرسانی کاربر
    public function testUpdateUser()
    {
        // ایجاد کاربر
        $userData = [
            'name' => 'علی',
            'email' => 'ali@example.com',
            'password' => 'password123'
        ];

        $created = $this->userModel->create($userData);

        // بروزرسانی
        $updated = $this->userModel->update($created['id'], [
            'name' => 'علی رضا',
            'role' => 'admin'
        ]);

        $this->assertNotNull($updated);
        $this->assertEquals('علی رضا', $updated['name']);
        $this->assertEquals('admin', $updated['role']);
    }

    // تست حذف کاربر
    public function testDeleteUser()
    {
        // ایجاد کاربر
        $userData = [
            'name' => 'سارا',
            'email' => 'sara@example.com',
            'password' => 'password123'
        ];

        $created = $this->userModel->create($userData);

        // حذف کاربر
        $deleted = $this->userModel->delete($created['id']);

        $this->assertTrue($deleted);

        // بررسی اینکه کاربر دیگر وجود ندارد
        $found = $this->userModel->find($created['id']);
        $this->assertNull($found);
    }

    // تست ایمیل تکراری
    public function testEmailExists()
    {
        // ایجاد کاربر
        $userData = [
            'name' => 'تست',
            'email' => 'test@example.com',
            'password' => 'password123'
        ];

        $this->userModel->create($userData);

        // بررسی وجود ایمیل
        $exists = $this->userModel->emailExists('test@example.com');
        $this->assertTrue($exists);

        $notExists = $this->userModel->emailExists('nonexistent@example.com');
        $this->assertFalse($notExists);
    }
}

🧪 تست Endpoint (Feature Test)

تست ویژگی (Feature Test) برای Endpointهای کامل API:



🧪
UserApiTest.php

PHP
8.2




<?php

use PHPUnit\Framework\TestCase;

class UserApiTest extends TestCase
{
    private $baseUrl = 'http://localhost:8000';
    private $authToken;

    // قبل از هر تست، توکن دریافت کن
    protected function setUp(): void
    {
        parent::setUp();

        // ثبت‌نام یا ورود برای دریافت توکن
        $response = $this->sendRequest('POST', '/auth/login', [
            'email' => 'admin@example.com',
            'password' => 'admin123'
        ]);

        if (isset($response['data']['token'])) {
            $this->authToken = $response['data']['token'];
        }
    }

    // متد کمکی برای ارسال درخواست
    private function sendRequest($method, $endpoint, $data = [])
    {
        $url = $this->baseUrl . $endpoint;
        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Accept: application/json',
            'Authorization: Bearer ' . $this->authToken
        ]);

        if (!empty($data)) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        }

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        return [
            'status_code' => $httpCode,
            'body' => json_decode($response, true)
        ];
    }

    // تست دریافت لیست کاربران
    public function testGetUsers()
    {
        $response = $this->sendRequest('GET', '/v2/users');

        $this->assertEquals(200, $response['status_code']);
        $this->assertEquals('success', $response['body']['status']);
        $this->assertArrayHasKey('data', $response['body']);
        $this->assertArrayHasKey('pagination', $response['body']['data']);
    }

    // تست ایجاد کاربر
    public function testCreateUser()
    {
        $userData = [
            'name' => 'کاربر تست',
            'email' => 'testuser@example.com',
            'password' => 'password123',
            'role' => 'user'
        ];

        $response = $this->sendRequest('POST', '/v2/users', $userData);

        $this->assertEquals(201, $response['status_code']);
        $this->assertEquals('success', $response['body']['status']);
        $this->assertEquals('کاربر تست', $response['body']['data']['name']);
    }

    // تست دریافت یک کاربر
    public function testGetUser()
    {
        // ابتدا یک کاربر ایجاد کن
        $userData = [
            'name' => 'کاربر جستجو',
            'email' => 'search@example.com',
            'password' => 'password123'
        ];

        $createResponse = $this->sendRequest('POST', '/v2/users', $userData);
        $userId = $createResponse['body']['data']['id'];

        // دریافت کاربر
        $response = $this->sendRequest('GET', '/v2/users/' . $userId);

        $this->assertEquals(200, $response['status_code']);
        $this->assertEquals('کاربر جستجو', $response['body']['data']['name']);
    }

    // تست اعتبارسنجی
    public function testValidationErrors()
    {
        $invalidData = [
            'name' => '',
            'email' => 'invalid-email',
            'password' => '123'
        ];

        $response = $this->sendRequest('POST', '/v2/users', $invalidData);

        $this->assertEquals(422, $response['status_code']);
        $this->assertEquals('error', $response['body']['status']);
        $this->assertArrayHasKey('errors', $response['body']);
    }

    // تست حذف کاربر
    public function testDeleteUser()
    {
        // ایجاد کاربر
        $userData = [
            'name' => 'کاربر حذف',
            'email' => 'delete@example.com',
            'password' => 'password123'
        ];

        $createResponse = $this->sendRequest('POST', '/v2/users', $userData);
        $userId = $createResponse['body']['data']['id'];

        // حذف کاربر
        $response = $this->sendRequest('DELETE', '/v2/users/' . $userId);

        $this->assertEquals(204, $response['status_code']);
    }
}

🎭 Mocking در تست

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



🎭
Mocking Example

PHP
8.2




<?php

use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\MockObject\MockObject;

class UserControllerTest extends TestCase
{
    /**
     * @var User|MockObject
     */
    private $userModelMock;
    
    private $controller;

    protected function setUp(): void
    {
        parent::setUp();

        // ایجاد Mock برای User Model
        $this->userModelMock = $this->createMock(User::class);

        // ایجاد کنترلر با Mock
        $this->controller = new UserController();
        
        // تزریق Mock به کنترلر (با Reflection)
        $reflection = new ReflectionClass($this->controller);
        $property = $reflection->getProperty('userModel');
        $property->setAccessible(true);
        $property->setValue($this->controller, $this->userModelMock);
    }

    // تست دریافت کاربر با Mock
    public function testShowUserWithMock()
    {
        // داده‌های مورد انتظار
        $expectedUser = [
            'id' => 1,
            'name' => 'یونس',
            'email' => 'younes@example.com'
        ];

        // تنظیم Mock برای متد find
        $this->userModelMock
            ->expects($this->once())
            ->method('find')
            ->with(1)
            ->willReturn($expectedUser);

        // اجرای متد
        $request = new Request();
        $response = $this->controller->show($request, [1]);

        // Assertions
        $this->assertEquals(200, $response->statusCode);
        $this->assertEquals('یونس', $response->body['data']['name']);
    }

    // تست خطای پیدا نشدن کاربر
    public function testShowUserNotFound()
    {
        // Mock برای بازگشت null
        $this->userModelMock
            ->expects($this->once())
            ->method('find')
            ->with(999)
            ->willReturn(null);

        $request = new Request();
        $response = $this->controller->show($request, [999]);

        $this->assertEquals(404, $response->statusCode);
        $this->assertEquals('User not found', $response->body['message']);
    }

    // تست ایجاد کاربر با Mock
    public function testStoreUserWithMock()
    {
        $userData = [
            'name' => 'کاربر جدید',
            'email' => 'new@example.com',
            'password' => 'password123'
        ];

        $expectedUser = $userData + ['id' => 10];

        // Mock برای متد create
        $this->userModelMock
            ->expects($this->once())
            ->method('create')
            ->with($userData)
            ->willReturn($expectedUser);

        // Mock برای emailExists
        $this->userModelMock
            ->expects($this->once())
            ->method('emailExists')
            ->willReturn(false);

        $request = new Request();
        $request->body = $userData;

        $response = $this->controller->store($request);

        $this->assertEquals(201, $response->statusCode);
        $this->assertEquals('کاربر جدید', $response->body['data']['name']);
    }
}

📝 Logging پیشرفته با Monolog

Monolog محبوب‌ترین کتابخانه لاگ‌گیری در PHP است:



📝
Monolog Setup

PHP
8.2




<?php

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\FirePHPHandler;

class AppLogger
{
    private static $instance = null;
    private $logger;

    private function __construct()
    {
        $this->logger = new Logger('api');

        // لاگ در فایل با چرخش روزانه
        $logFile = __DIR__ . '/../storage/logs/api.log';
        $rotatingHandler = new RotatingFileHandler($logFile, 30, Logger::INFO);
        $this->logger->pushHandler($rotatingHandler);

        // لاگ خطاها در فایل جداگانه
        $errorLogFile = __DIR__ . '/../storage/logs/errors.log';
        $errorHandler = new StreamHandler($errorLogFile, Logger::ERROR);
        $this->logger->pushHandler($errorHandler);

        // در محیط توسعه، لاگ را در FirePHP نمایش بده
        if ($_ENV['APP_ENV'] === 'development') {
            $this->logger->pushHandler(new FirePHPHandler());
        }
    }

    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function info($message, $context = [])
    {
        $this->logger->info($message, $context + $this->getDefaultContext());
    }

    public function error($message, $context = [])
    {
        $this->logger->error($message, $context + $this->getDefaultContext());
    }

    public function warning($message, $context = [])
    {
        $this->logger->warning($message, $context + $this->getDefaultContext());
    }

    public function debug($message, $context = [])
    {
        if ($_ENV['APP_ENV'] === 'development') {
            $this->logger->debug($message, $context + $this->getDefaultContext());
        }
    }

    private function getDefaultContext()
    {
        return [
            'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'method' => $_SERVER['REQUEST_METHOD'] ?? 'unknown',
            'uri' => $_SERVER['REQUEST_URI'] ?? 'unknown',
            'user_id' => $GLOBALS['auth_user']['user_id'] ?? 'guest'
        ];
    }
}

// استفاده در کنترلر
public function store($request)
{
    try {
        // ... کد ایجاد کاربر ...
        
        AppLogger::getInstance()->info('User created', ['user_id' => $user['id']]);
        
        return Response::created($user);
        
    } catch (Exception $e) {
        AppLogger::getInstance()->error('Failed to create user', [
            'error' => $e->getMessage(),
            'trace' => $e->getTraceAsString()
        ]);
        
        return Response::error('Failed to create user', 500);
    }
}

🔧 دیباگ با Xdebug و Postman

ابزارهای دیباگ حرفه‌ای برای عیب‌یابی API:

ابزار کاربرد تنظیمات
Xdebug دیباگ Step-by-Step در کد zend_extension=xdebug.so
xdebug.mode=debug
xdebug.start_with_request=yes
Postman تست و دیباگ API Collections, Environments
Console (View → Show Postman Console)
Whoops نمایش خطاهای زیبا composer require filp/whoops
VarDumper دیباگ متغیرها composer require symfony/var-dumper
dump($variable);


🔧
Debugging Tools Setup

PHP
8.2




<?php

// ========== ۱. راه‌اندازی Whoops ==========
require_once __DIR__ . '/../vendor/autoload.php';

use Whoops\Run;
use Whoops\Handler\PrettyPageHandler;

$whoops = new Run();
$whoops->pushHandler(new PrettyPageHandler());

// فقط در محیط توسعه فعال کن
if ($_ENV['APP_ENV'] === 'development') {
    $whoops->register();
}

// ========== ۲. VarDumper برای دیباگ سریع ==========
use Symfony\Component\VarDumper\VarDumper;

function dd(...$vars)
{
    foreach ($vars as $var) {
        VarDumper::dump($var);
    }
    exit(1);
}

// استفاده در کنترلر (فقط در محیط توسعه)
public function debugExample($request)
{
    if ($_ENV['APP_ENV'] === 'development') {
        dd($request->getBody(), $_SERVER);
    }
    
    // ... کد اصلی ...
}

// ========== ۳. Postman Console ==========
// در Postman، Console را باز کن (View → Show Postman Console)
// تمام درخواست‌ها و پاسخ‌ها را در Console مشاهده کن

// ========== ۴. Xdebug + VS Code ==========
// تنظیمات launch.json در VS Code:
// {
//     "version": "0.2.0",
//     "configurations": [
//         {
//             "name": "Listen for XDebug",
//             "type": "php",
//             "request": "launch",
//             "port": 9003
//         }
//     ]
// }

📊 Postman Collection و تست خودکار

Postman Collection به شما امکان می‌دهد تست‌های API را ذخیره و به اشتراک بگذارید:



📊
Postman Collection (JSON)

JSON




// Postman Collection - بخشی از تعریف
{
    "info": {
        "name": "API User Management",
        "version": "1.0.0"
    },
    "item": [
        {
            "name": "Login",
            "request": {
                "method": "POST",
                "header": [
                    {
                        "key": "Content-Type",
                        "value": "application/json"
                    }
                ],
                "body": {
                    "mode": "raw",
                    "raw": "{\n    \"email\": \"admin@example.com\",\n    \"password\": \"admin123\"\n}"
                },
                "url": {
                    "raw": "{{base_url}}/auth/login",
                    "host": ["{{base_url}}"],
                    "path": ["auth", "login"]
                }
            },
            "response": []
        },
        {
            "name": "Get Users",
            "request": {
                "method": "GET",
                "header": [
                    {
                        "key": "Authorization",
                        "value": "Bearer {{auth_token}}"
                    }
                ],
                "url": {
                    "raw": "{{base_url}}/v2/users?page=1&limit=10",
                    "host": ["{{base_url}}"],
                    "path": ["v2", "users"],
                    "query": [
                        {
                            "key": "page",
                            "value": "1"
                        },
                        {
                            "key": "limit",
                            "value": "10"
                        }
                    ]
                }
            }
        }
    ],
    "variable": [
        {
            "key": "base_url",
            "value": "http://localhost:8000"
        },
        {
            "key": "auth_token",
            "value": ""
        }
    ]
}

🔴 خطاهای رایج در تست و دیباگ

  • ❌ اشتباه: تست‌هایی که به ترتیب اجرا وابسته هستند.
  • ✅ درست: هر تست باید مستقل باشد و به ترتیب اجرا وابسته نباشد.
  • ❌ اشتباه: استفاده از دیتابیس تولید در تست.
  • ✅ درست: از دیتابیس جداگانه یا SQLite In-Memory استفاده کن.
  • ❌ اشتباه: لاگ کردن اطلاعات حساس مثل رمز عبور.
  • ✅ درست: اطلاعات حساس را از لاگ حذف کن یا Mask کن.
  • ❌ اشتباه: Xdebug را در محیط تولید فعال گذاشتن.
  • ✅ درست: Xdebug فقط در محیط توسعه فعال باشد.

💎 نکات کلیدی درس

  • ✅ PHPUnit ابزار اصلی تست‌نویسی در PHP است.
  • ✅ تست‌ها به دو دسته Unit Test و Feature Test تقسیم می‌شوند.
  • ✅ Mocking برای شبیه‌سازی وابستگی‌ها در تست استفاده می‌شود.
  • ✅ Monolog کتابخانه استاندارد لاگ‌گیری در PHP است.
  • ✅ Xdebug ابزار قدرتمند دیباگ Step-by-Step است.
  • ✅ Postman Collection تست‌های API را سازماندهی و به اشتراک می‌گذارد.
  • ✅ لاگ‌گیری و دیباگ در محیط توسعه با محیط تولید متفاوت است.

🛠️ پروژه عملی درس نهم

مهمان عزیز، حالا تست‌های کامل برای API محصولات بنویس!

مسئله:

یک مجموعه تست کامل برای API محصولات پیاده‌سازی کن:

  • ✅ تست‌های Unit برای مدل Product (CRUD).
  • ✅ تست‌های Feature برای Endpointهای Product.
  • ✅ Mock کردن وابستگی‌ها در تست‌ها.
  • ✅ راه‌اندازی Monolog برای لاگ‌گیری.
  • ✅ Postman Collection کامل برای API محصولات.
  • ✅ راه‌اندازی Xdebug برای دیباگ.

🧪 راه‌حل پروژه (پاسخ)

مراحل پیاده‌سازی:

  • 1️⃣ کلاس ProductTest برای تست مدل ایجاد کن.
  • 2️⃣ کلاس ProductApiTest برای تست Endpointها ایجاد کن.
  • 3️⃣ از Mock برای شبیه‌سازی دیتابیس در تست‌ها استفاده کن.
  • 4️⃣ کلاس AppLogger را با Monolog راه‌اندازی کن.
  • 5️⃣ Postman Collection را برای تمام Endpointهای محصولات ایجاد کن.
  • 6️⃣ Xdebug را در VS Code راه‌اندازی کن.

📝 تمرین‌های عملی

🧪 تمرین ۱ (ساده):

تست‌های Unit برای مدل Product بنویس. شامل تست‌های create، find، update، delete و getAll.

🧪 تمرین ۲ (متوسط):

تست‌های Feature برای API محصولات بنویس. شامل تست‌های GET، POST، PUT و DELETE با اعتبارسنجی.

🧪 تمرین ۳ (چالشی):

یک سیستم تست خودکار با GitHub Actions یا GitLab CI راه‌اندازی کن. هر بار که کد به مخزن Push می‌شود، تست‌ها به صورت خودکار اجرا شوند.


🏁 جمع‌بندی درس

مهمان عزیز، در این درس یاد گرفتی:

  • ✅ چگونه PHPUnit را نصب و راه‌اندازی کنی.
  • ✅ چگونه تست‌های Unit و Feature بنویسی.
  • ✅ چگونه از Mocking برای شبیه‌سازی وابستگی‌ها استفاده کنی.
  • ✅ چگونه با Monolog لاگ‌گیری حرفه‌ای انجام دهی.
  • ✅ چگونه با Xdebug و Postman دیباگ کنی.
  • ✅ چگونه Postman Collection برای تست API ایجاد کنی.

حالا API شما کاملاً تست شده و قابل اعتماد است. در درس بعدی، پروژه نهایی را با هم پیاده‌سازی می‌کنیم. آماده‌ای؟ 🚀


🗺️ نقشه راه دوره: شما اینجا هستید!

برای اینکه بدانی دقیقاً کجای مسیر هستی و چه درس‌هایی در انتظار توست، به جدول زیر نگاه کن. درس فعلی با رنگ متفاوت مشخص شده است.

درس عنوان درس آنچه یاد می‌گیرید
۱ مفاهیم پایه API HTTP، REST، معماری Client-Server
۲ پیاده‌سازی RESTful API با PHP خام مسیریابی، متدها، پارامترها، پاسخ‌های JSON
۳ مدیریت داده‌ها و پایگاه‌داده اتصال PDO، CRUD، تراکنش‌ها، Pagination
۴ اعتبارسنجی و احراز هویت JWT، Roles/Permissions، Middleware
۵ امنیت API پیشرفته OWASP Top 10، SQLi، XSS، CSRF، Rate Limiting
۶ مستندسازی API با OpenAPI Swagger، OpenAPI Specification، مستندات تعاملی
۷ Caching و افزایش عملکرد Redis، ETag، Cache-Control، Query Optimization
۸ API Versioning و مدیریت چرخه حیات استراتژی‌های نسخه‌بندی، Deprecation، Backward Compatibility
۹ تست و دیباگ API حرفه‌ای PHPUnit، Mocking، Logging، Postman Collection
۱۰ پروژه نهایی: API فروشگاهی پروژه کامل فروشگاه اینترنتی با تمام قابلیت‌ها

🔗 لینک‌های مرتبط

👤
نویسنده
📅
تاریخ انتشار 30 مرداد 1405
🔄
آخرین بروزرسانی 30 مرداد 1405

نظر خود را بنویسید

با ثبت نظر، به بهبود محتوای ما کمک کنید

امتیاز شما به این مطلب (اختیاری)
برای امتیازدهی، روی ستاره‌ها کلیک کنید
نام شما در سایت نمایش داده می‌شود
ایمیل شما محفوظ می‌ماند
0 کاراکتر | حداقل ۱۰ کاراکتر
نظر سازنده و مفید بنویسید
ارسال دیدگاه: +۵ امتیاز
امتیازدهی: +۲ امتیاز
نشان شما: مبتدی
لطفاً از کلمات محترمانه استفاده کنید. دیدگاه‌های توهین‌آمیز حذف می‌شوند.
⚠️

هشدار مهم!

برای اتصال به درگاه پرداخت و انجام تراکنش، لطفاً VPN یا فیلترشکن خود را خاموش کنید.

⏳ ادامه در
5
ثانیه