Phần này tìm hiểu tạo ra một máy chủ HTTP đơn giản với lớp HttpListener
.
Thông thường để có đầy đủ các tính năng khi triển khai ứng dụng chúng ta có thể sử dụng
dịch vụ HTTP hoàn chỉnh như IIS
, Apache HTTPD
,
Nginx
hay Kestrel
.
Tuy vậy, để nhanh chóng có thể tạo dịch vụ lắng nghe các yêu cầu HTTP gửi đến
(Http Request Message)
và trả về Response
(Http Message Response)
thích hợp thì có thể dùng đến HttpListener
Lớp HttpListener
Lớp HttpListener giúp tạo ra dịch vụ với giao thức HTTP, nó lắng nghe các yêu cầu HTTP Request gửi đến và trả về HTTP Respone. Dịch vụ HTTP hoạt động và tồn tại cùng với sự tồn tại của đối tượng HttpListener.
Một số phương thức thuộc tính của lớp HttpListener
Thành viên | Diễn tả |
---|---|
IsSupported |
Phương thức tĩnh kiểu bool, cho biết máy có hỗ trợ HttpListener hay không |
Prefixes |
Thuộc tính, nó chứa các chuỗi cấu trúc URI mà HttpListener làm việc. Ví dụ để
nó làm việc trên cổng 8080, giao thức http:// thì chuỗi đó là http://*:8080/
và làm việc trên cổng 443 thì chuỗi là https://*/
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://*:8080/");
listener.Prefixes.Add("https://*/");
|
Start() |
Bắt đầu cho phép nhận các yêu cầu (HTTP Request) gửi đến. |
Stop() |
Dừng dịch vụ |
GetContextAsync() |
Chờ kết tạo một kết nối client gửi đến và thực hiện bất đồng bộ nhận thông tin gửi đến
bởi client nếu có. Trả về Task với thuộc tính Result khi kết thúc là đối
tượng kiểu HttpListenerContext chứa thông tin gửi đến (request) và thông tin
sẽ trả về response , khi đối tượng này huỷ thì respone sẽ trả về cho clien truy cập.
HttpListenerContext context = await listener.GetContextAsync();
HttpListenerRequest request = context.Request; // chứa thông tin gửi đến
HttpListenerResponse response = context.Response;// các nội dung trả về thiết lập vào respone
|
Code tạo máy chủ Http cơ bản như sau:
// Mảng chứa địa chỉ Http lắng nghe
// http = giao thức http, * = ip bất kỳ, 8080 = cổng lắng nghe
string[] prefixes = new string[] { "http://*:8080/" };
HttpListener listener = new HttpListener();
if (!HttpListener.IsSupported) throw new Exception ("Hệ thống hỗ trợ HttpListener.");
if (prefixes == null || prefixes.Length == 0) throw new ArgumentException ("prefixes");
foreach (string s in prefixes) {
listener.Prefixes.Add (s);
}
Console.WriteLine ("Server start ...");
// Http bắt đầu lắng nghe truy vấn gửi đến
listener.Start();
// Vòng lặp chấp nhận và xử lý các client kết nối
do
{
// Chấp nhận khi có cliet kết nối đế
HttpListenerContext context = await listener.GetContextAsync();
// ....
// Xử lý context - đọc thông tin request, ghi thông tin response
// ... ví dụ như sau:
var response = context.Response; // lấy HttpListenerResponse
var outputstream = response.OutputStream; // lấy Stream lưu nội dung gửi cho client
context.Response.Headers.Add("content-type", "text/html"); // thiết lập respone header
byte[] buffer = System.Text.Encoding.UTF8.GetBytes("Hello world!"); // dữ liệu content
response.ContentLength64 = buffer.Length;
await outputstream.WriteAsync(buffer,0,buffer.Length); // viết content ra stream
outputstream.Close(); // Đóng stream (gửi về cho cliet)
}
while (listener.IsListening);
Ví dụ tạo máy chủ HTTP đơn giản bằng HttpListener
// Chạy một HTTP Server, prefixes example: new string[] { "http://*:8080/" } | |
class MyHttpServer | |
{ | |
private HttpListener listener; | |
public MyHttpServer(params string[] prefixes) | |
{ | |
if (!HttpListener.IsSupported) | |
throw new Exception ("Máy không hỗ trợ HttpListener."); | |
if (prefixes == null || prefixes.Length == 0) | |
throw new ArgumentException ("prefixes"); | |
// Khởi tạo HttpListener | |
listener = new HttpListener (); | |
foreach (string prefix in prefixes) | |
listener.Prefixes.Add(prefix); | |
} | |
public async Task StartAsync() | |
{ | |
// Bắt đầu lắng nghe kết nối HTTP | |
listener.Start(); | |
do | |
{ | |
try | |
{ | |
Console.WriteLine($"{DateTime.Now.ToLongTimeString()} : waiting a client connect"); | |
// Một client kết nối đến | |
HttpListenerContext context = await listener.GetContextAsync(); | |
await ProcessRequest(context); | |
} | |
catch (Exception ex) | |
{ | |
Console.WriteLine(ex.Message); | |
} | |
Console.WriteLine("..."); | |
} | |
while (listener.IsListening); | |
} | |
// Xử lý trả về nội dung tùy thuộc vào URL truy cập | |
// / hiện thị dòng Hello World | |
// /stop dừng máy chủ | |
// /json trả về một nội dung json | |
// /anh2.png trả về một file ảnh | |
// /requestinfo thông tin truy vấn | |
async Task ProcessRequest(HttpListenerContext context) | |
{ | |
HttpListenerRequest request = context.Request; | |
HttpListenerResponse response = context.Response; | |
Console.WriteLine($"{request.HttpMethod} {request.RawUrl} {request.Url.AbsolutePath}"); | |
// Lấy stream / gửi dữ liệu về cho client | |
var outputstream = response.OutputStream; | |
switch (request.Url.AbsolutePath) | |
{ | |
case "/requestinfo": | |
{ | |
// Gửi thông tin về cho Client | |
context.Response.Headers.Add("content-type", "text/html"); | |
context.Response.StatusCode = (int) HttpStatusCode.OK; | |
string responseString = this.GenerateHTML(request); | |
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString); | |
response.ContentLength64 = buffer.Length; | |
await outputstream.WriteAsync(buffer,0,buffer.Length); | |
} | |
break; | |
case "/": | |
{ | |
byte[] buffer = System.Text.Encoding.UTF8.GetBytes("Hello world!"); | |
response.ContentLength64 = buffer.Length; | |
await outputstream.WriteAsync(buffer,0,buffer.Length); | |
} | |
break; | |
case "/stop": | |
{ | |
listener.Stop(); | |
Console.WriteLine("stop http"); | |
} | |
break; | |
case "/json": | |
{ | |
response.Headers.Add("Content-Type", "application/json"); | |
var product = new { | |
Name = "Macbook Pro", | |
Price = 2000, | |
Manufacturer = "Apple" | |
}; | |
string jsonstring = JsonConvert.SerializeObject(product); | |
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(jsonstring); | |
response.ContentLength64 = buffer.Length; | |
await outputstream.WriteAsync(buffer,0,buffer.Length); | |
} | |
break; | |
case "/anh2.png": | |
{ | |
response.Headers.Add("Content-Type", "image/png"); | |
byte[] buffer = await File.ReadAllBytesAsync("anh2.png"); | |
response.ContentLength64 = buffer.Length; | |
await outputstream.WriteAsync(buffer,0,buffer.Length); | |
} | |
break; | |
default: | |
{ | |
response.StatusCode = (int)HttpStatusCode.NotFound; | |
byte[] buffer = System.Text.Encoding.UTF8.GetBytes("NOT FOUND!"); | |
response.ContentLength64 = buffer.Length; | |
await outputstream.WriteAsync(buffer,0,buffer.Length); | |
} | |
break; | |
} | |
// switch (request.Url.AbsolutePath) | |
// Đóng stream để hoàn thành gửi về client | |
outputstream.Close(); | |
} | |
// Tạo nội dung HTML trả về cho Client (HTML chứa thông tin về Request) | |
public string GenerateHTML (HttpListenerRequest request) { | |
string format = @"<!DOCTYPE html> | |
<html lang=""en""> | |
<head> | |
<meta charset=""UTF-8""> | |
{0} | |
</head> | |
<body> | |
{1} | |
</body> | |
</html>"; | |
string head = "<title>Test WebServer</title>"; | |
var body = new StringBuilder (); | |
body.Append ("<h1>Request Info</h1>"); | |
body.Append ("<h2>Request Header:</h2>"); | |
// Header infomation | |
var headers = from key in request.Headers.AllKeys | |
select $"<div>{key} : {string.Join(",", request.Headers.GetValues(key))}</div>"; | |
body.Append (string.Join ("", headers)); | |
//Extract request properties | |
body.Append ("<h2>Request properties:</h2>"); | |
var properties = request.GetType ().GetProperties (); | |
foreach (var property in properties) { | |
var name_pro = property.Name; | |
string value_pro; | |
try { | |
value_pro = property.GetValue (request).ToString (); | |
} catch (Exception e) { | |
value_pro = e.Message; | |
} | |
body.Append ($"<div>{name_pro} : {value_pro}</div>"); | |
}; | |
string html = string.Format (format, head, body.ToString ()); | |
return html; | |
} | |
} | |
Sử dụng
static async Task Main(string[] args)
{
var server = new MyHttpServer(new string[] {"http://*:8080/"});
await server.StartAsync();
}
Truy cập thử với http://localhost:8080/requestinfo
Tham khảo mã nguồn WebListener (git) hoặc tải về ex035