مبحث Restful Api، همواره یکی از مباحث مورد علاقه ی توسعه دهندگان فریم ورکهای مدرن جاوا اسکریپت و هم چنین اپلیکیشنهای موبایل بوده است. Rest مخفف Representational State Transfer و در واقع، یک معماری برای تبادل اطلاعات از طریق شبکه و به وسیله ی وب سرویس است که اولین بار توسط Roy Fielding در سال 2000 میلادی معرفی شد. اصطلاح Restful هم به طور خلاصه، به وب سرویس هایی که از این معماری استفاده میکنند، اطلاق میشود.
پکیج Passport در لاراول یکی از پکیجهای رسمی لاراول است که اعتبارسنجی وب سرویسها را با استفاده از معماری Rest، برای توسعه دهندگان لاراول آسان میکند. این پکیج با استفاده از پروتکل OAuth2 و همین طور الگوریتم JWT، ایجاد توکنهای دسترسی شخصی یا Personal Access Tokenها را برای اعتبارسنجی کاربران لاگین شده ممکن میسازد. این توکن منحصربه فرد، با هر درخواست کاربر، به سمت سرور ارسال میشود و دسترسی به مسیرهای حفاظت شده را برای کاربر ممکن میسازد.
در این مقاله، قصد داریم تا شما را به طور عملی، با نحوه ی ایجاد توکنهای دسترسی شخصی و نحوه ی ذخیره سازی و استفاده از آن به وسیله ی پکیج Passport در لاراول آشنا کنیم؛ تا بتوانید اعتبارسنجی وب سرویسهای اپلیکیشن خود را به آسانترین شکل، با استفاده از ویژگیهای جذاب این پکیج انجام دهید. پس تا انتهای این مقاله با آموزش استفاده از پکیج Passport در لاراول، با ما همراه باشید .
نیازمندیهای اولیه
برای شروع آموزش لاراول پاسپورت، به دانش کافی در زمینه ی لاراول نیاز دارید.
نصب لاراول
به پوشه ی دلخواه خود بروید و دستور زیر را برای نصب لاراول اجرا کنید:
composer create-project --prefer-dist laravel/laravel blog
با دستور زیر میتوانید پروژه را اجرا کنید:
php artisan serve
پس از اجرای این دستور، پروژه بر روی پورت 8000 لوکال هاست در دسترس است. با وارد کردن آدرس https://localhost:8000 در نوار آدرس مرورگر، با این صفحه مواجه میشوید.
راه اندازی دیتابیس
اکنون که لاراول را نصب کرده اید، قدم بعدی، برقراری ارتباط با دیتابیس است. ابتدا یک دیتابیس دلخواه را ایجاد کنید و سپس مقادیر متغیرهای زیر را در فایل env. پروژه بروز کنید.
اکنون دیتابیس آماده است. در قدم بعدی باید پس از نصب پکیج، جداول موردنظر خود را ایجاد کنیم.
نصب و راه اندازی پکیج
با اجرای دستور زیر در مسیر پروژه، پکیج Passport را نصب کنید:
composer require laravel/passport
ایجاد جداول
پس از آن که نصب پکیج به پایان رسید، با اجرای دستور زیر، جداولی که برای کار با Passport در لاراول نیاز است، به منظور ذخیره ی توکن ها، کلاینتها و... را ایجاد کنید. فایل Migration مربوط به این جداول، در محتوای پکیج، موجود در پوشه ی vendor قرار گرفته اند.
Php artisan migrate
ایجاد کلیدهای رمزنگاری
پس از اجرای دستور فوق، به منظور ایجاد کلیدهای رمزنگاری توکن ها، دستور زیر را اجرا کنید.
php artisan passport:install
HasApiTokens
سپس به فایل User.php در مسیر app/Models مراجعه کرده و Trait با نام HasApiTokens را به مدل مربوطه اضافه کنید.
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasFactory, Notifiable, HasApiTokens;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
یکی از فواید افزودن این Trait به مدل User، دسترسی مدل به تعدادی متد کمکی است که میتوان با استفاده از این متدها کارهایی نظیر ایجاد توکن، دسترسی به توکن ایجاد شده برای کاربر، بررسی Scope توکنها و... انجام داد. در ادامه از این متدهای کمکی استفاده خواهیم کرد.
ایجاد مسیرها
اکنون باید متد ()boot در فایل AuthServiceProvider را به شکل زیر بروز کنیم:
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
use Laravel\Passport\Passport;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Passport::routes();
}
}
افزودن خط فوق به متد ()boot، مسیرهای مورد نیاز برای ایجاد و لغو توکنها را به اپلیکیشن اضافه میکند.
Api Guard
اکنون نوبت آن رسیده که با استفاده از TokenGuard در Passport، درخواستهای Api ورودی را احراز هویت کنیم. با مراجعه به فایل auth.php در پوشه ی config، درایور مربوط به Guard با نام api را به شکل زیر، برابر با مقدار passport قرار دهید.
تاریخ انقضای توکنها در پکیج Passport در لاراول، به صورت پیش فرض طولانی مدت و برابر یک سال است. ما میخواهیم این مقدار پیش فرض را برای توکنهای پروژه که از نوع توکن دسترسی شخصی هستند، به یک روز تغییر دهیم. این کار را با متد ()boot در فایل AuthServiceProvider به صورت زیر انجام میدهیم.
<?php
namespace App\Providers;
use Carbon\Carbon;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
use Laravel\Passport\Passport;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Passport::routes();
Passport::personalAccessTokensExpireIn(Carbon::now()->addHours(24));
}
}
تا این جا تنظیمات موردنیاز Passport را به طور کامل انجام داده ایم. اکنون وقت آن فرا رسیده است که فرآیند احراز هویت کاربران را انجام دهیم.
ایجاد کنترلرها
ابتدا با اجرای دستور زیر، کنترلر AuthController را ایجاد میکنیم. این کنترلر، وظیفه ی پاسخگویی به درخواستهای مربوط به ثبت نام و ورود کاربر را بر عهده دارد.
php artisan make:controller Api/v1/AuthController
دقت کنید که کنترلر را در مسیر app/Http/Controllers/Api/v1 ایجاد کرده ایم. پوشه ی Api را به کنترلرهای مربوط به وب سرویسها اختصاص داده ایم. اما نکته ی اصلی، قرار دادن کنترلر در پوشه ی v1 است که مربوط به نسخه ی اولیه ی اپلیکیشنی است که ایجاد میکنیم. با انجام این کار، در صورت نیاز به ارائه ی نسخههای جدیدتر اپلیکیشن، کاربران نسخههای قدیمی تر، در اجرای اپلیکیشن با مشکلی مواجه نخواهند شد، چرا که هر نسخه، مسیرها و کنترلرهای مخصوص به خود را دارد.
ایجاد Resource
ویژگی Resource از نسخه ی 5.5، به لاراول اضافه شده است. Resource لایه ای است که بین مدل Eloquent و پاسخ Json خروجی قرار میگیرد و تبدیل اطلاعات مدل یا کالکشن مدلها به خروجی Json را آسان میکند. همچنین میتوانید اطلاعات مدلها را به دلخواه خود فیلتر کنید. با اجرای دستور زیر یک Resource برای مدل User ایجاد میکنیم.
php artisan make:resource v1/UserResource
می بینید که Resource را در پوشه ی v1 قرار داده ایم تا بتوانیم Resourceهای نسخههای مختلف اپلیکیشن را از هم جدا کنیم. اکنون Resource ایجاد شده را به شکل زیر ویرایش کنید.
<?php
namespace App\Http\Resources\v1;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
];
}
}
می توانید هرکدام از اطلاعات کاربر را که نیاز دارید، به شکل فوق در قالب یک آرایه ی Json برگردانید. نحوه ی استفاده از Resource ایجاد شده را در ادامه خواهید دید.
ایجاد متدها
اکنون متدهای موردنیاز را در کنترلر AuthController موجود در مسیر app/Http/Controllers/Api/v1، پیاده سازی میکنیم.
متد ثبت نام
<?php
namespace App\Http\Controllers\Api\v1;
use App\Http\Controllers\Controller;
use App\Http\Resources\v1\UserResource;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
class AuthController extends Controller
{
public function register(Request $request)
{
$data = $request->all();
$validator = Validator::make($request->all(), [
'name' => 'required|max:55|unique:users',
'email' => 'required|email|unique:users',
'password' => 'required|confirmed'
]);
if ($validator->fails()) {
return response()->json($validator->errors()->first(), 422);
}
$data['password'] = bcrypt($request->password);
$user = User::create($data);
$accessToken = $user->createToken('UserToken')->accessToken;
return response()->json([
'user' => new UserResource($user),
'token' => $accessToken,
'token_type' => 'Bearer'
]);
}
}
همان طور که میبینید، ابتدا اطلاعات ارسالی از سوی کاربر را اعتبارسنجی میکنیم. سپس رمز عبور ارسالی کاربر را با استفاده از تابع کمکی ()bcrypt رمزنگاری میکنیم. در ادامه کاربر جدید را ایجاد کرده و با استفاده از تابع کمکی ()createToken، توکن منحصربه فرد مربوط به کاربر را ایجاد میکنیم. این تابع در واقع، یک نمونه از کلاس PersonalAccessTokenResult را ایجاد میکند و توکن ایجاد شده در خصوصیت accessToken قابل دسترس است. این تابع به عنوان پارامتر اول، یک رشته را به عنوان نام توکن میپذیرد. این رشته یک نام دلخواه است. در پارامتر دوم نیز میتوان یک آرایه را برای مشخص کردن scopeهای توکن به این تابع پاس داد که افزودن آن اختیاری است.
در ادامه نیز خروجی Json را به شکل آرایه، به سمت کلاینت ارسال میکنیم. در کلید اول این آرایه، یک نمونه از Resource را که ایجاد کرده ایم، قرار داده و مدل کاربر ایجاد شده را نیز به آن پاس میدهیم. در کلید دوم توکن و در کلید سوم نوع توکن را قرار میدهیم. توکنهای ایجاد شده در این حالت، از نوع Bearer هستند که در Authorization header درخواست قرار میگیرند. توکنهای Bearer بخشی از استاندارد پروتکل 0Auth2 هستند.
متدهای ورود و خروج
پس از متد register، متد login و logout را نیز در همان کنترلر به شکل زیر تعریف میکنیم.
<?php
namespace App\Http\Controllers\Api\v1;
use App\Http\Controllers\Controller;
use App\Http\Resources\v1\UserResource;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
class AuthController extends Controller
{
public function register(Request $request)
{
$data = $request->all();
$validator = Validator::make($request->all(), [
'name' => 'required|max:55|unique:users',
'email' => 'required|email|unique:users',
'password' => 'required|confirmed'
]);
if ($validator->fails()) {
return response()->json($validator->errors()->first(), 422);
}
$data['password'] = bcrypt($request->password);
$user = User::create($data);
$accessToken = $user->createToken('UserToken')->accessToken;
return response()->json([
'user' => new UserResource($user),
'token' => $accessToken,
'token_type' => 'Bearer'
]);
}
public function login(Request $request)
{
$data = $request->all();
$validator = Validator::make($request->all(), [
'email' => 'email|required',
'password' => 'required'
]);
if ($validator->fails()) {
return response()->json($validator->errors()->first(), 422);
}
if (!auth()->attempt($data)) {
return response()->json('ایمیل یا رمز عبور اشتباه است.', 422);
}
$user = auth()->user();
$tokenResult = $user->createToken('userToken');
$tokenModel = $tokenResult->token;
if ($request->remember_me)
$tokenModel->expires_at = Carbon::now()->addWeeks(1);
$tokenModel->save();
return response()->json([
'user' => new UserResource($user),
'token' => $tokenResult->accessToken,
'token_type' => 'Bearer'
]);
}
public function logout(Request $request)
{
/** @var User $user
*/
$request->user()->token()->revoke();
return response()->json('شما با موفقیت خارج شدید.');
}
}
با مشاهده ی متد login، میبینید که ابتدا ایمیل و رمز عبور ارسالی کاربر را اعتبارسنجی میکنیم. سپس با استفاده از تابع کمکی ()auth، عملیات احراز هویت را انجام میدهیم. نمونه ی ساخته شده از کلاس PersonalAccessTokenResult را در متغیر tokenResult ذخیره میکنیم. خصوصیت token در این کلاس، حاوی مدل رکورد ایجاد شده در جدول oauth_access_tokens در دیتابیس است که توکنهای دسترسی شخصی را در خود ذخیره میکند. در صورتی که کاربر گزینه ی Remember Me یا مرا به خاطر بسپار را انتخاب کرده باشد، میتوان تاریخ انقضای توکن ایجاد شده را به دلخواه تغییر داد. در اینجا ما تاریخ انقضا را برابر یک هفته قرار میدهیم و سپس با استفاده از متد ()save، مدل مربوطه را بروزرسانی میکنیم. در انتها نیز، مانند متد register، اطلاعات را در قالب آرایه ی Json به سمت کاربر ارسال میکنیم.
برای logout نیز ابتدا با استفاده از متد ()token، مدل رکورد مربوط به توکن ارسالی با درخواست کاربر را گرفته و با استفاده از متد ()revoke این مدل، آن را باطل میکنیم. سپس میتوانیم یک پیام دلخواه نیز به سمت کلاینت ارسال کنیم.
ایجاد مسیرها
برای تکمیل فرآیند نوشتن وب سرویس ها، باید مسیرهای موردنیاز را به شکل زیر در فایل api.php در پوشه ی routes تعریف کنیم.
<?php
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::group(['prefix' => 'v1'], function (){
Route::post('/register', [\App\Http\Controllers\Api\v1\AuthController::class, 'register']);
Route::post('/login', [\App\Http\Controllers\Api\v1\AuthController::class, 'login']);
Route::middleware('auth:api')->get('/logout', [\App\Http\Controllers\Api\v1\AuthController::class, 'logout']);
});
همان طور که میبینید، مسیرها را با استفاده از پیشوند v1 نسخه بندی کرده ایم تا در صورت ارائه ی نسخه ی بالاتر، مسیرهای مربوط به نسخههای قدیمی تحت تاثیر قرار نگیرند. هم چنین برای مسیر logout یا خروج، از یک Middleware با نام auth:api استفاده کرده ایم. شما میتوانید از این Middleware برای هر مسیری که نیاز به یک توکن معتبر دارد، استفاده کنید تا دسترسی به این مسیرها محدود شود. قسمت دوم نام این Middleware، مربوط به Guard تعریف شده در فایل AuthServiceProvider است. در صورتی که نیاز به احراز هویت انواع مختلفی از کاربران را دارید و هر نوع کاربر نیز از مدل Eloquent متفاوتی استفاده میکند، میتوانید برای هر کدام، یک Guard در فایل AuthServiceProvider ایجاد کنید و سپس نام Guard دلخواه را در نام Middleware فوق جایگزین کنید.
تست وب سرویس ها
پس از اجرای مراحل فوق، اکنون باید وب سرویس هایی که ایجاد کرده ایم را تست کنیم. ما این کار را با استفاده از نرم افزار Postman انجام میدهیم. میتوانید این نرم افزار را به صورت رایگان از وب سایت Postman دانلود کنید.
اجرای اپلیکیشن
ابتدا مطمئن شوید که با استفاده از Wamp یا Xampp، ماژول MySql را فعال کرده اید سپس، با استفاده از دستور زیر، اپلیکیشن را اجرا کنید.
php artisan serve
ثبت نام
نرم افزار Postman را باز کنید. با کلیک بر روی گزینه ی Create a request، یک پنجره ی ارسال درخواست ایجاد کنید. مسیر ثبت نام را در کادر مربوط به خود وارد کنید و متد Post را انتخاب کنید. در قسمت Body، با انتخاب x-www-form-urlencoded، پارامترهای لازم برای ثبت نام را وارد کنید. مقادیر پارامترها دلخواه هستند.
پس از کلیک بر روی گزینه ی Send، پاسخ موردنظر بنا به اطلاعات ارسالی به صورت آرایه ی Json به شما نشان داده میشود. در صورت رعایت کلیه ی نکات ذکر شده در این مقاله و همچنین استفاده از مقادیر فوق، پاسخ موفقیت آمیز به صورت زیر برایتان نمایش داده میشود.
در این حالت، یک کاربر با اطلاعات فوق در دیتابیس شما ایجاد شده است.
ورود
برای ورود نیز پنجره ی دیگری را باز کرده و مانند قبل، مسیر، متد و پارامترهای لازم را وارد کنید. سپس بر روی دکمه ی Send کلیک کنید. در صورت ورود صحیح اطلاعات، با پاسخ موفقیت آمیز زیر روبرو میشوید.
خروج
چه پس از ثبت نام و چه پس از ورود، در صورت دریافت موفقیت آمیز توکن، میتوانید به تمام مسیرهای ویژه ی کاربران دسترسی داشته باشید. کافی است که توکن ایجاد شده را در در قسمت Header Authorization کپی کنید.
به عنوان مثال، برای ارسال درخواست خروج باید توکن ایجاد شده را به همراه درخواست خود به سمت سرور ارسال کنید. برای این منظور، یک پنجره ی جدید باز کرده و مانند قبل، مسیر را وارد و متد را انتخاب کنید. این بار، در قسمت Authorization، در سمت چپ و از منوی آبشاری Type، گزینه ی Bearer Token را انتخاب کنید. سپس توکن را در کادر سمت راست کپی کنید. پس از کلیک بر روی گزینه ی Send، توکن با موفقیت باطل میشود.
در این مقاله از سری مقالات آموزش لاراول، نحوه ی احراز هویت وب سرویسهای Restful را با ایجاد توکنهای دسترسی شخصی و استفاده از پکیج Passport در لاراول آموختیم و توانستیم به صورت عملی با جزییات استفاده از این پکیج آشنا شویم. همچنین توانستیم وب سرویسهای Restful ایجاد شده را با استفاده از نرم افزار Postman تست کنیم و از اجرای موفقیت آمیز آنها مطمئن شویم. با شیوه ی نسخه بندی در نوشتن وب سرویس آشنا شدیم و نحوه ی ایجاد Resource برای تبدیل مدل یا کالکشن به خروجی Json را فراگرفتیم. شما به همین شیوه ی ذکر شده در این مقاله، میتوانید وب سرویسهای دلخواهتان را برای هر اپلیکیشنی که نیاز دارید، ایجاد کنید و با استفاده از ایجاد توکنهای دسترسی شخصی، این وب سرویسها را ایمن کنید.
خوشحال میشویم نظرات، تجربیات و سوالات خود را با ما و سایر کاربران سون لرن به اشتراک بگذارید.
اگر به یادگیری بیشتر لاراول علاقه داری میتوانی در دوره آموزش لاراول کاربردی (بسته پروژه محور) شرکت کنی، این دوره شامل ۱۲ پروژه کاربردی و پر استفاده در دنیای واقعی است، که تمامی پروژهها به صورت کامل برنامه نویسی خواهند شد، تا دانشجو بتواند با روند ایجاد و تکمیل پروژه به صورت کامل آشنا شود.
سلام
خیلی ممنون از مطالب خوب و با ارزشی ک گزاشتید
مشکل حل شد
۰۸ تیر ۱۴۰۱، ۱۰:۱۸
این روش در passport 10 به بعد کار نمیکنه
۲۶ آذر ۱۴۰۰، ۰۹:۲۸
سلام این ارور برای چیه؟
$ php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Illuminate\Database\QueryException
SQLSTATE[42S01]: Base table or view already exists: 1050 Table &#39;users&#39; already
exists (SQL: create table `users` (`id` bigint unsigned not null auto_increment
primary key, `name` varchar(255) not null, `email` varchar(255) not null, `emai
l_verified_at` timestamp null, `password` varchar(255) not null, `remember_token
` varchar(100) null, `created_at` timestamp null, `updated_at` timestamp null) d
efault character set utf8mb4 collate &#39;utf8mb4_unicode_ci&#39;)
at C:\xampp\htdocs\blog\vendor\laravel\framework\src\Illuminate\Database\Conne
ction.php:703
699▕ // If an exception occurs when attempting to run a query, we&#39;ll
format the error
700▕ // message to include the bindings with SQL, which will make th
is exception a
701▕ // lot more helpful to the developer instead of just the databa
se&#39;s errors.
702▕ catch (Exception $e) {
➜ 703▕ throw new QueryException(
704▕ $query, $this-&gt;prepareBindings($bindings), $e
705▕ );
706▕ }
707▕ }
1 C:\xampp\htdocs\blog\vendor\laravel\framework\src\Illuminate\Database\Conn
ection.php:492
PDOException::(&quot;SQLSTATE[42S01]: Base table or view already exists: 1050 T
able &#39;users&#39; already exists&quot;)
2 C:\xampp\htdocs\blog\vendor\laravel\framework\src\Illuminate\Database\Conn
ection.php:492
PDOStatement::execute()
نازنین کریمی مقدم۲۷ آذر ۱۴۰۰، ۰۸:۴۵
درود
احتمالا وسطش بنا به دلایلی عملیات نیمه تمام مونده.
اگر در حال کار روی دیتابیس لوکال هستید بد نیست جدول رو پاک کنید و دوباره امتحان کنید. یا با استفاده از دستور زیر یکبار ریست انجام بدید:
php artisan migrate:reset
حسین شیری نژاد۲۶ مهر ۱۴۰۰، ۰۶:۵۹
خیلی خیلی خوب بود قربون شما
واقعا به این آموزشهای خوب به زبان فارسی نیاز هست سریع کار آدمو را میندازه.
نازنین کریمی مقدم۲۶ مهر ۱۴۰۰، ۱۲:۳۸
درود
خوشحالیم مقاله براتون مفید بوده دوست عزیز
mohammadhussein۱۵ اردیبهشت ۱۴۰۰، ۱۲:۲۱
اموزش کاملی بود . خیلی ممنون
احمد۱۴ اردیبهشت ۱۴۰۰، ۰۹:۲۹
واقعا عالی بود.
محمدحسین طاهری۱۶ بهمن ۱۳۹۹، ۱۴:۵۷
بسیار عالی و کامل بود، موفق باشید..
شروع رایگان یادگیری برنامه نویسی
کلیک کنید 👇
دوره الفبای برنامه نویسی با هدف انتخاب زبان برنامه نویسی مناسب برای شما و پاسخگویی به سوالات متداول در شروع یادگیری موقتا رایگان شد: