☕ NEW! 完成新手任務即可參加抽獎!LINE 星巴克禮券等你拿,名額有限!        🎉 推廣活動:邀請好友註冊 DevLearn,累積推薦抽 LINE 星巴克禮券! 活動詳情 →        🔥 活動期間 2026/4/1 - 5/31 |已有 0 人參加       
Server 初學

Web Server 基礎概念

什麼是 Web Server?

💡 比喻:餐廳的櫃台接待 想像一間餐廳:

  • 客人(瀏覽器)走進餐廳
  • 櫃台接待(Web Server)迎接客人
  • 客人說:「我要看菜單」(HTTP Request)
  • 接待員把菜單拿給客人(HTTP Response)
  • 如果客人點了一道需要現做的菜(動態內容),接待員會把訂單送到廚房(應用程式)
  • 如果客人只要一瓶水(靜態檔案),接待員直接從冰箱拿

Web Server 就是這個櫃台接待——負責接收請求、分派工作、回傳結果。

Web Server 的核心工作

客人(瀏覽器)        櫃台接待(Web Server)        廚房(應用程式)
    |                       |                           |
    |--- 我要看首頁 ------->|                           |
    |                       |--- 這是動態頁面 --------->|
    |                       |<-- 做好了,給你 HTML ------|
    |<-- 這是你要的首頁 ----|                           |
    |                       |                           |
    |--- 我要 logo.png ---->|                           |
    |<-- 直接給你圖片 ------|  (靜態檔案不用問廚房)     |

Kestrel vs IIS vs Nginx

三大 Web Server 比較

// 三種常見 Web Server 的角色比較
// Kestrel:ASP.NET Core 內建的輕量級 Server
// IIS:Windows 專用的老牌 Server
// Nginx:跨平台的高效能 Server

特性              Kestrel          IIS              Nginx
─────────────────────────────────────────────────────────────
平台              跨平台           僅 Windows        跨平台          // 部署環境的限制
效能              高               中等             非常高          // 處理請求的速度
內建於 .NET       是               否               否              // 是否開箱即用
反向代理          不建議直接暴露    可以             最常用          // 面對外網的能力
靜態檔案處理      普通             好               非常好          // 處理圖片CSS等
SSL/TLS          支援             支援             最佳            // 加密連線能力

💡 比喻:接待員的等級

  • Kestrel 像是餐廳的實習接待員——做事很快,但經驗不足,不適合獨自面對大量客人
  • IIS 像是資深接待員——經驗豐富,但只在特定餐廳(Windows)工作
  • Nginx 像是五星級飯店的大堂經理——處理大量客人游刃有餘,還能協調多間餐廳

最佳實踐架構

// 生產環境推薦架構
// Nginx 在前面負責接待(反向代理)
// Kestrel 在後面負責處理(應用程式)

外部請求 → Nginx(反向代理)→ Kestrel(ASP.NET Core 應用)

// 這樣的好處:
// 1. Nginx 擅長處理靜態檔案和 SSL
// 2. Kestrel 專心處理業務邏輯
// 3. Nginx 提供額外的安全防護

靜態檔案 vs 動態內容

靜態檔案

// Program.cs 中啟用靜態檔案服務
var builder = WebApplication.CreateBuilder(args); // 建立應用程式建構器
var app = builder.Build();                         // 建構應用程式

app.UseStaticFiles(); // 啟用 wwwroot 資料夾中的靜態檔案服務

// 靜態檔案的存放位置:
// wwwroot/
//   ├── css/          // 樣式表檔案
//   │   └── site.css  // 網站主要樣式
//   ├── js/           // JavaScript 檔案
//   │   └── app.js    // 前端邏輯
//   ├── images/       // 圖片檔案
//   │   └── logo.png  // 網站 Logo
//   └── favicon.ico   // 網站圖示

動態內容

// 動態內容由 Controller 或 Minimal API 產生
// 每次請求都可能回傳不同的結果

app.MapGet("/api/time", () =>        // 定義一個 API 端點
{
    return DateTime.Now.ToString();     // 每次呼叫回傳當下時間(動態)
});

app.MapGet("/api/users/{id}", (int id) =>  // 根據使用者 ID 查詢
{
    return $"使用者 {id} 的資料";            // 根據參數回傳不同結果
});

Port、Binding、Listen

什麼是 Port?

💡 比喻:大樓的房間號碼 IP 位址像是一棟大樓的地址,Port 就是大樓裡的房間號碼

  • 80 號房 → HTTP 接待室
  • 443 號房 → HTTPS 加密接待室
  • 5000 號房 → 你的 ASP.NET Core 應用
  • 一棟大樓(一台電腦)可以有 65535 個房間

設定 Port 和 Binding

// Program.cs 中設定監聽的 Port
var builder = WebApplication.CreateBuilder(args); // 建立建構器

// 方法一:透過程式碼設定
builder.WebHost.UseUrls(
    "http://localhost:5000",   // 監聽 HTTP 5000 Port
    "https://localhost:5001"   // 監聽 HTTPS 5001 Port
);

// 方法二:透過命令列參數
// dotnet run --urls "http://localhost:8080"    // 指定單一 Port
// dotnet run --urls "http://*:80"              // 監聽所有網路介面的 80 Port
// launchSettings.json 中的設定
{
  "profiles": {                          // 啟動設定檔
    "MyApp": {                           // 應用程式名稱
      "commandName": "Project",        // 使用專案啟動
      "applicationUrl": "https://localhost:5001;http://localhost:5000",  // 監聽的網址
      "environmentVariables": {          // 環境變數
        "ASPNETCORE_ENVIRONMENT": "Development"  // 開發環境
      }
    }
  }
}

HTTP Request/Response 完整流程

一個請求的完整旅程

// 當你在瀏覽器輸入 https://myapp.com/products 時發生了什麼?

步驟 1:DNS 解析                        // 把網址轉成 IP 位址
  myapp.com → 123.45.67.89             // 就像查電話簿

步驟 2:TCP 連線                        // 建立通訊通道
  三次握手 (SYN → SYN-ACK → ACK)       // 就像打電話先確認對方在

步驟 3:TLS 握手(HTTPS)               // 建立加密通道
  交換憑證、協商加密方式                  // 就像雙方約定暗號

步驟 4:發送 HTTP Request               // 送出請求
  GET /products HTTP/1.1                // 我要看商品列表
  Host: myapp.com                       // 目標主機
  Accept: text/html                     // 我想要 HTML 格式

步驟 5:Server 處理請求                  // Web Server 接收並處理
  Nginx → Kestrel → Controller → DB    // 經過層層處理

步驟 6:回傳 HTTP Response              // 送回結果
  HTTP/1.1 200 OK                       // 狀態碼 200 表示成功
  Content-Type: text/html               // 內容是 HTML
  <html>...</html>                      // 實際的網頁內容

步驟 7:瀏覽器渲染                       // 把 HTML 變成你看到的畫面

常見 HTTP 狀態碼

// 狀態碼就像櫃台接待的回應
200 OK              // 沒問題,這是你要的東西
301 Moved           // 搬家了,去新地址找
304 Not Modified    // 跟上次一樣,用你的快取就好
400 Bad Request     // 你的要求我看不懂
401 Unauthorized    // 你還沒登入,請先登入
403 Forbidden       // 你沒有權限看這個
404 Not Found       // 找不到你要的東西
500 Server Error    // 我們這邊出問題了,不是你的錯
502 Bad Gateway     // 後面的廚房(應用程式)沒回應
503 Unavailable     // 我們暫時休息中

appsettings.json 環境設定

// appsettings.json - 基本設定(所有環境共用)
{
  "Logging": {                              // 日誌設定區塊
    "LogLevel": {                           // 日誌等級設定
      "Default": "Information",           // 預設記錄 Information 以上
      "Microsoft.AspNetCore": "Warning"   // ASP.NET Core 只記錄 Warning 以上
    }
  },
  "AllowedHosts": "*",                    // 允許所有主機名稱存取
  "ConnectionStrings": {                    // 資料庫連線字串
    "DefaultConnection": "Server=localhost;Database=MyDb;Trusted_Connection=true"  // 本機 SQL Server
  },
  "AppSettings": {                          // 自訂應用程式設定
    "SiteName": "我的網站",               // 網站名稱
    "MaxUploadSize": 10485760              // 最大上傳大小(10MB)
  }
}
// appsettings.Development.json - 開發環境專用(會覆蓋基本設定)
{
  "Logging": {                              // 開發環境的日誌設定
    "LogLevel": {                           // 日誌等級
      "Default": "Debug"                  // 開發時記錄更詳細的 Debug 等級
    }
  },
  "AppSettings": {                          // 開發環境的應用設定
    "SiteName": "我的網站(開發版)"       // 開發版網站名稱
  }
}
// 在程式中讀取設定
var builder = WebApplication.CreateBuilder(args); // 建構器會自動載入設定檔

// 讀取設定值
var siteName = builder.Configuration["AppSettings:SiteName"];        // 用冒號分隔階層
var maxSize = builder.Configuration.GetValue<int>("AppSettings:MaxUploadSize"); // 轉型讀取

// 用強型別讀取設定(推薦做法)
builder.Services.Configure<AppSettings>(                              // 綁定設定到類別
    builder.Configuration.GetSection("AppSettings")                  // 指定設定區塊
);

// 在 Controller 或 Service 中注入使用
public class HomeController : Controller                              // 控制器
{
    private readonly AppSettings _settings;                           // 設定欄位

    public HomeController(IOptions<AppSettings> options)              // 透過 DI 注入
    {
        _settings = options.Value;                                    // 取得設定值
    }
}

🤔 我這樣寫為什麼會錯?

❌ 錯誤一:在生產環境直接暴露 Kestrel

// 錯誤:讓 Kestrel 直接面對外部網路
builder.WebHost.UseUrls("http://*:80");  // 直接監聽 80 Port 對外服務

// 問題:Kestrel 缺乏完整的安全防護
// 沒有速率限制、沒有請求過濾、沒有 DDoS 防護

// ✅ 正確做法:前面加上 Nginx 反向代理
// Nginx 負責對外,Kestrel 只監聽 localhost
builder.WebHost.UseUrls("http://localhost:5000"); // 只監聽本機,由 Nginx 轉發

❌ 錯誤二:把敏感資訊寫在 appsettings.json

// 錯誤:把密碼和金鑰直接寫在設定檔中
{
  "ConnectionStrings": {
    "Default": "Server=db;Password=MyP@ssw0rd123;"  // 密碼明文寫在設定檔!
  },
  "ApiKeys": {
    "Stripe": "sk_live_abc123"                       // API 金鑰直接暴露!
  }
}
// 這些設定檔可能被提交到 Git,所有人都看得到

// ✅ 正確做法:使用環境變數或 User Secrets
// 開發環境用 dotnet user-secrets set "ApiKeys:Stripe" "sk_live_abc123"
// 生產環境用環境變數 export ConnectionStrings__Default="Server=db;..."

❌ 錯誤三:不理解環境設定的覆蓋順序

// 錯誤:以為 appsettings.json 的值不會被覆蓋
// 實際的載入順序(後面的會覆蓋前面的):
// 1. appsettings.json                    // 基礎設定(最先載入)
// 2. appsettings.{Environment}.json      // 環境設定(覆蓋基礎)
// 3. User Secrets(開發環境)             // 開發密鑰(覆蓋環境)
// 4. 環境變數                            // 系統變數(覆蓋密鑰)
// 5. 命令列參數                          // 最後載入(最高優先)

// ✅ 正確理解:後載入的設定會覆蓋先載入的
// 所以生產環境的環境變數會覆蓋 appsettings.json 中的同名設定

💡 大家的想法 · 0

載入中...
💬 即時聊天室 🟢 0 人在線
😀 😎 🤓 💻 🎮 🎸 🔥
➕ 新問題
📋 我的工單
💬 LINE 社群
🔒
需要註冊才能使用此功能
註冊帳號即可解鎖測驗、遊戲、簽到、筆記下載等所有功能,完全免費!
免費註冊