سلام، سرور!؛ از یک صفحه تا اولین پاسخ واقعی HTTP
در این درس ۷ بخش
در این پروژه فقط یک صفحه میسازیم، اما پاسخ داخل آن واقعی است: یک نام آزمایشی مینویسید، دکمهٔ «دریافت سلام» را میزنید و برنامهٔ Node.js جملهای برایتان برمیگرداند. دکمهٔ دیگری هم وضعیت همان برنامه را میپرسد. قرار نیست حساب کاربری، فروشگاه یا پایگاه داده بسازیم. هدف این است که ارتباط صفحه و سرور را با چشم ببینید و بتوانید آن را روی رایانه و سپس اوبونتو اجرا کنید. در پایان مسیر، همین پروژه را با HTTPS باز میکنید؛ یک بار با گواهی و تمدید خودکار Let’s Encrypt و یک بار با روش نصب دستی گواهی.
صفحهٔ وب، وبسرور و API چه فرقی دارند؟
صفحهٔ وب چیزی است که مرورگر نمایش میدهد: عنوان، رنگ، دکمه و محل پاسخ. فایل HTML ساختار این صفحه را مشخص میکند و CSS ظاهر آن را میسازد. وبسرور برنامهای است که منتظر درخواست شبکه میماند و پاسخ میدهد. وقتی نشانی پروژه را باز میکنید، این برنامه فایل صفحه را میفرستد؛ وقتی دکمهای را میزنید، میتواند بهجای یک صفحهٔ کامل، فقط داده برگرداند. API در این مثال قرارداد همین گفتوگوی دادهای است: چه نشانیای را با چه روشی صدا بزنیم، چه چیزی بفرستیم و چه ساختاری پس بگیریم.
Node.js محیط اجرای #javascript بیرون از مرورگر است؛ خود زبان تازهای نیست. اوبونتو نیز سیستمعاملی است که برنامه روی آن اجرا میشود. روی رایانهٔ ویندوز هم میتوان همین کد را برای تمرین اجرا کرد. بعداً فایلها را به اوبونتوی آزمایشگاهی منتقل میکنیم تا برنامه مستقل از پنجرهٔ ما اجرا شود. یک #function در سرور مسئول پاسخدادن به درخواست میشود؛ جاوااسکریپت دیگری در مرورگر مسئول گرفتن نام و نمایش پاسخ است. مشترکبودن زبان به معنی یکیبودن محل اجرا یا اجازههای این دو برنامه نیست.
مرورگر میتواند رنگ جعبهٔ پاسخ را عوض کند، ولی در این پروژه جملهٔ نهایی سلام را سرور میسازد. برای دیدن تفاوت، بعداً هنگام ارسال نام به بخش Network ابزار توسعهٔ مرورگر نگاه میکنیم. درخواست با بدنهای شامل نام «سارا» فرستاده میشود و پاسخی شامل یک پیام برمیگردد. اگر برنامهٔ سرور را متوقف کنید، ظاهر بازشده ممکن است هنوز روی صفحه بماند، اما دکمه پاسخ تازهای نمیگیرد. این آزمایش ساده نشان میدهد دیدهشدن صفحه و آمادهبودن API دو مشاهدهٔ متفاوتاند. همان تفاوت را هنگام عیبیابی هم حفظ کنید.
قرارداد کوچک پروژه را پیش از کد بشناسید
درخواست GET برای خواندن صفحه، فایل ظاهر و فایل جاوااسکریپت استفاده میشود. مسیر /api/status نیز با GET وضعیت برنامه را به شکل داده برمیگرداند. درخواست POST به /api/greet یک نام میفرستد و پیام سلام میگیرد. «مسیر» قسمت نشانی پس از میزبان و پورت است؛ /api/greet نام یک فایل روی دیسک نیست. برنامه تصمیم میگیرد با دیدن مسیر و روش، چه کاری انجام دهد. بنابراین بازکردن /api/greet در نوار نشانی مرورگر که معمولاً GET میفرستد، معادل زدن دکمهٔ فرم با POST نیست.
پاسخ دادهای با قالب #json منتقل میشود؛ یعنی متنی با ساختار مشخص که میتواند ویژگی message و مقدار متنی آن را داشته باشد. مرورگر آن را به داده تبدیل میکند و پیام را نمایش میدهد. نام واردشده در این پروژه در پایگاه داده یا فایل ثبت نمیشود و برنامه برای آن حساب نمیسازد. از نام ساختگی استفاده کنید. موفقیت این نمونه به معنی آمادهبودن سامانهٔ ثبت مشتری یا ورود کاربر نیست. هدف ما دیدن و آزمودن یک درخواست است؛ در محصول واقعی، ذخیرهسازی و اجازهٔ دسترسی باید جداگانه طراحی شوند.
پیش از نوشتن فایل، محیط را آماده کنید
یک Node.js پشتیبانیشده در شاخهٔ ۲۴ یا بالاتر، ویرایشگری مانند VS Code و مرورگر جدید کافی است. پروژه وابستگی npm خارجی ندارد. فعلاً به دامنه، سرور اجارهای یا Docker نیاز ندارید. اگر node --version شماره نشان نمیدهد، نخست مشکل محیط را حل کنید؛ تغییر کد صفحه جای نصب محیط اجرا را نمیگیرد. راهنمای نصب ویندوز و فضای کار در درسهای قبلی، مراحل و خطاهای PATH را توضیح میدهد. فایلهای تمرین را در پوشهای تازه نگه دارید تا با پروژهٔ قبلی مخلوط نشوند. داشتن ابزار بیشتر، جای تشخیص پوشهٔ درست را نمیگیرد.
اگر با ویندوز شروع میکنید، در صورت نیاز این درسهای مشخص را مرور کنید:
نصب Node.js و npm: https://www.darsavand.com/courses/nodejs-windows-backend-start?lesson=4d78897a-eb60-4f73-a5e4-4535d4294a57
فضای کار VS Code: https://www.darsavand.com/courses/nodejs-windows-backend-start?lesson=5c9fbc95-bdce-46f2-87a9-62b86110a926
اگر از حالا روی اوبونتو کار میکنید، نصب Node از مسیر ثابت در این درس آمده است: https://www.darsavand.com/courses/nodejs-backend-ubuntu-deployment?lesson=dd7977ff-4b0f-41a0-a222-050ce5693620
اینها مسیر جبران پیشنیازند؛ لازم نیست برای فراموشکردن یک دستور، کل دوره را دوباره بخوانید. پیش از ادامه باید یک فایل با پسوند واقعی بسازید، آن را ذخیره کنید و فرمان را در پوشهٔ درست اجرا کنید.
روی ویندوز، PowerShell را باز و بلوک زیر را اجرا کنید. پوشهٔ hello-http در فضای شخصی ساخته میشود؛ اگر از قبل هست، ابتدا محتوا را بررسی کنید و فایل نامرتبط را بازنویسی نکنید. نقطه در code . یعنی همین پوشه. اگر فرمان code شناخته نشد، از منوی File > Open Folder در VS Code همان پوشه را باز کنید؛ این خطا بهتنهایی خرابی Node نیست. خروجی نسخه و مسیر را یادداشت کنید. New-Item اینجا ابزار نصب نیست و فقط پوشه میسازد؛ تنظیمات سیستمی یا دسترسی مدیر را تغییر نمیدهیم.
# روی رایانهٔ ویندوز، در PowerShell
$projectDir = Join-Path $HOME 'hello-http'
New-Item -ItemType Directory -Path $projectDir -Force | Out-Null
Set-Location -LiteralPath $projectDir
node --version
Get-Location
code .اگر محیط شما اوبونتو است، دستورهای PowerShell را آنجا ننویسید؛ بلوک زیر متعلق به Bash است. mkdir پوشه را میسازد و cd پوشهٔ کاری را عوض میکند. sudo لازم نیست، چون در محل شخصی فایل مینویسیم. اگر Node طبق درس قبلی زیر /opt/node-current نصب شده، ابتدا PATH همان نشست را طبق راهنما تنظیم کنید تا node به نسخهٔ مورد نظر برسد. نام و محیط هر ترمینال را بشناسید؛ رنگ تیره یا روشن پنجره نمیگوید فرمان متعلق به کدام سیستم است. برای این مرحله فقط یکی از مسیرهای ویندوز یا اوبونتو را انتخاب کنید.
# روی اوبونتو، در Bash کاربر معمولی
mkdir -p "$HOME/hello-http"
cd "$HOME/hello-http"
node --version
pwdآزمایش کوتاه: یک پاسخ، پیش از ساخت صفحهٔ کامل
آزمایش زیر اختیاری است و نقش سرور را مستقل از ظاهر نشان میدهد. فایل تازهٔ hello-first.mjs را در همان پوشه بسازید و کد کامل را ذخیره کنید. پسوند mjs انتخاب صریح ماژول جاوااسکریپت است. import ابزار ساخت سرور را از کتابخانهٔ داخلی Node میگیرد؛ برای آن بسته نصب نمیکنیم. اگر import را نمیشناسید، #modules-import-export را مرور کنید. این فایل سرور اصلی صفحه نیست. یک مرحلهٔ کوچک و قابل مشاهده است که پس از انجام آن متوقفش میکنیم و سراغ پروژهٔ اصلی میرویم. نام فایل را با server.mjs درس سوم یکی نکنید.
import { createServer } from 'node:http';
const server = createServer((request, response) => {
response.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
response.end('سلام! این پاسخ از اولین سرور من آمده است.');
});
server.listen(3100, '127.0.0.1', () => {
console.log('Open http://127.0.0.1:3100');
});فایل را ذخیره و در ترمینال داخل پوشه، node hello-first.mjs را اجرا کنید؛ این دستور در PowerShell و Bash یکسان نوشته میشود. پیام Open و نشانی را میبینید. http://127.0.0.1:3100 را در مرورگر باز کنید؛ باید متن فارسی پاسخ دیده شود. کد ۲۰۰ پاسخ موفق را نشان میدهد و Content-Type میگوید بدنه متن سادهٔ UTF-8 است، پس مرورگر انتظار HTML ندارد. پورت این آزمایش ۳۱۰۰ است؛ پروژهٔ اصلی از ۳۰۰۰ استفاده میکند تا فایلها اشتباه نشوند. اگر پورت اشغال بود، برنامهٔ خودتان را بررسی کنید و کورکورانه سراغ پایاندادن همهٔ فرایندها نروید.
نشانی 127.0.0.1 لوپبک است؛ یعنی گفتوگو با همان دستگاه. این IP عمومی VPS نیست. اگر کد را روی سرور دور اجرا کردهاید، نوشتن این نشانی در مرورگر ویندوز به ویندوز خودتان اشاره میکند؛ دسترسی از بیرون در فصل اوبونتو توضیح داده میشود. فرایند پس از چاپ نشانی باز میماند تا درخواست بگیرد؛ این وضعیت الزاماً هنگ نیست. در همان ترمینال Ctrl+C بزنید تا همین آزمایش متوقف شود. برای اجرای سرور و ارسال دستور آزمایش، میتوانید دو ترمینال داشته باشید؛ اولی سرور را نگه میدارد و دومی کارهای دیگر را انجام میدهد.
خطا را مرحلهای بررسی کنید و مسیر ادامه را بشناسید
اگر فایل پیدا نشد، پوشهٔ جاری و پسوند واقعی را ببینید؛ شاید نام آن hello-first.mjs.txt شده باشد. اگر EADDRINUSE دیدید، یک برنامه پورت را گرفته است؛ ترمینال تمرین خودتان را پیدا و همان را متوقف کنید. اگر مرورگر Connection refused داد، ترمینال سرور را بررسی کنید: برنامه اجرا مانده یا با خطا بسته شده؟ برای این تست محلی، بازکردن فایروال عمومی لازم نیست. تغییر همزمان پورت و نصب و فایل، تشخیص را دشوار میکند. هر بار یک فرضیه و یک مشاهده داشته باشید؛ بعد از هر اصلاح، همان درخواست قبلی را دوباره بفرستید.
پروژهٔ اصلی، app.mjs و server.mjs و package.json را در ریشه خواهد داشت؛ داخل public نیز index.html و style.css و app.js قرار میگیرند. app.mjs رفتار درخواستها را تعریف میکند و server.mjs شروع و توقف فرایند را برعهده میگیرد. app.js داخل public در مرورگر اجرا میشود و با app.mjs سرور یک فایل نیست. در درس دوم سه فایل صفحه را کامل مینویسیم و در درس سوم سرور را اضافه میکنیم؛ تا آن موقع، انتظار پاسخ کامل فرم را نداریم. این ترتیب کمک میکند هنگام شکست بدانید ساختار، ظاهر یا ارتباط را باید بررسی کنید.
تمرین، پاسخ و معیار عبور
متن پاسخ hello-first.mjs را تغییر و صفحه را تازه کنید. اگر متن قدیمی ماند، فایل را ذخیره و فرایند Node را متوقف و دوباره اجرا کنید؛ این آزمایش بارگذاری خودکار کد ندارد. سپس بعد از Ctrl+C همان نشانی را باز کنید؛ نبود پاسخ جدید طبیعی است، چون برنامهای گوش نمیدهد. معیار عبور، دیدن یک پاسخ و توضیح تفاوت فایل، فرایند، نشانی و پورت است. گزارشی کوتاه شامل محیط، فرمان، خروجی و روش توقف بنویسید. در درسهای بعد، هنگام بررسی API و گواهی نیز همین عادت ثبت انتظار و مشاهده به کارتان میآید.
منابع رسمی برای مطالعهٔ بیشتر
Node.js — Introduction: https://nodejs.org/learn/getting-started/introduction-to-nodejs
Node.js — Run scripts: https://nodejs.org/learn/command-line/run-nodejs-scripts-from-the-command-line
MDN — Your first website: https://developer.mozilla.org/en-US/docs/Learn_web_development/Getting_started/Your_first_website
تألیف، سناریو و مثالهای آموزشی: تحریریه درسآوند. منابع انگلیسی برای بررسی فنی و مطالعهٔ بیشتر معرفی شدهاند.
متن درسهای عمومی را بدون ورود بخوانید.