使用Nancy 為Winform中增加web server功能
組件
- Nancy.Hosting.Self.dll
- Nancy.dll
- Newtonsoft.Json.dll
Nancy 的兩個庫應該選用v1的最后版本號, 不要使用v2版, v2版架構有較大變化但文檔又不完善, 而且已經停止開發. Nancy.Hosting.Self 庫可以幫助我們在console或winforms程序中增加web server功能, 而不用專門部署到IIS等服務器中.
綁定地址的說明:
- 推薦綁定 localhost, 本機和其他機器都能訪問.
- 如果綁定 127.0.0.1, 則只能通過 127.0.0.1 本機訪問, 其他機器無法訪問
- 如果綁定 0.0.0.0, 一般的web框架會綁定本機的所有網卡, 這樣web server能監聽任意網絡的請求, 但對于Nancy框架, 我測試其效果和127.0.0.1一樣.
Nancy module
Nancy module 類似于 controller 類, 用來定義endpoints, 一個類只要繼承自 NancyModule, 框架就會自動掃描到, 并在程序啟動后自動完成注冊綁定.
Nancy 路由支持常見的Http方法, 包括 DELETE GET POST PUT HEAD OPTIONS PATCH.
源碼
using Nancy;
using Nancy.Extensions;
using Nancy.Hosting.Self;
using Nancy.Session;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
private NancyHost host;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//Nancy Self Host 必須加上 AutomaticUrlReservationCreation, 否則 host.Start()會報異常
HostConfiguration hostConfigs = new HostConfiguration();
hostConfigs.UrlReservations.CreateAutomatically = true;
hostConfigs.RewriteLocalhost = true;
// 創建 NancyHost 實例, 推薦使用 localhost 而不是 127.0.0.1 或 0.0.0.0 寫法
host = new NancyHost(new Uri("http://localhost:18080"), new DefaultNancyBootstrapper(), hostConfigs);
// 啟動 NancyHost
host.Start();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// 停止 NancyHost
host.Stop();
}
}
/// <summary>
/// 使用NancyModule來定義路由
/// </summary>
public class CustomNancyModule : NancyModule
{
private static readonly XDeclaration _defaultDeclaration = new XDeclaration("1.0", null, null);
/// <summary>
/// 自定義的 OnError handler
/// </summary>
/// <param name="nancyContext"></param>
/// <param name="ex"></param>
/// <returns></returns>
private dynamic MyOnError(NancyContext nancyContext, Exception ex)
{
Console.Error.WriteLine(ex.Message );
var response = new Response();
response.StatusCode = HttpStatusCode.InternalServerError;
response.Contents = (stream) =>
{
using ( var writer = new StreamWriter(stream)) {
writer.Write(ex.Message );
}
};
return response ;
}
//構造函數中增加endPoint設定, 通過父類的 base() 參數設定 url base path
public CustomNancyModule():base("")
{
// 路由 /hello
Get["/hello"] = parameters => "Hello, World!";
// 路由 /hello2, 使用 streamReader 獲取 body 文本
Get["/hello2"] = parameters =>
{
using (StreamReader streamReader = new StreamReader(this.Context.Request.Body))
{
string body = streamReader.ReadToEnd();
Console.WriteLine(body);
}
return "Hello, World!";
};
// 路由 /hello2, 使用 streamReader 獲取 body 文本
Get["/hello3"] = parameters =>
{
using (StreamReader streamReader = new StreamReader(this.Context.Request.Body))
{
string body = streamReader.ReadToEnd();
Console.WriteLine(body);
}
return "Hello, World!";
};
// 路由 /hello3, 使用 Request.Body.AsString() 獲取 body 文本
Get["/hello3"] = parameters =>
{
string body = this.Request.Body.AsString();
Console.WriteLine(body);
return "Hello, World!";
};
//路由 pattern
Get["/product/{category}"] = parameters => "My category is " + parameters.category;
//路由 pattern, 限制類型為int
Get["/favoriteNumber/{value:int}"] = parameters =>
{
return "So your favorite number is " + parameters.value + "?";
};
// 路由 /data , POST json
Post["/data"] = parameters =>
{
// 獲取 POST 的 JSON 字符串
var jsonStr = this.Request.Body.AsString();
//使用 Newtonsoft 將 json字符串轉成 JObject 對象
JObject jobject = (JObject)JsonConvert.DeserializeObject(jsonStr);
// 返回一個響應,可以根據需要進行處理
return Response.AsJson(new { Message = "JSON received successfully" });
};
// 路由 /data , POST Xml
Post["/xmldata"] = parameters =>
{
// 獲取 POST 的 XML 字符串
var xmlStr = this.Request.Body.AsString();
// 返回一個響應,可以根據需要進行處理
return Response.AsJson(new { Message = "Xml received successfully" });
};
// would capture routes like /products/1034 sent as a DELETE request
Delete[@"/products/(?<id>[\d]{1,7})"] = parameters =>
{
return 200;
};
// 定義 The post-request hook, 可在這里重寫response
After += ctx =>
{
Console.WriteLine("After");
};
//定義 The pre-request hook, 如果該函數返回一個response, 則 pipeline 將不傳遞到目標 endpoint
Before += ctx =>
{
Console.WriteLine("Before");
NancyContext c = ctx as NancyContext;
Console.WriteLine("攔截器捕獲:" + c.Request.Body.AsString());
return null;
};
//定義 OnError handler
OnError += MyOnError;
}
}
}
測試代碼
=======================
GET http://localhost:18080/hello HTTP/1.1
content-type: application/text
abc
=======================
GET http://localhost:18080/hello2 HTTP/1.1
content-type: application/text
abc
=======================
GET http://localhost:18080/hello3 HTTP/1.1
content-type: application/text
abc
=======================
GET http://localhost:18080/product/popular HTTP/1.1
content-type: application/json
=======================
# json 請求
POST http://localhost:18080/data HTTP/1.1
content-type: application/json
{
"name": "sample",
"time": "Wed, 21 Oct 2015 18:27:50 GMT"
}
=======================
# XML 請求
POST http://localhost:18080/xmldata HTTP/1.1
Content-Type: application/xml
<request>
<name>sample</name>
<time>Wed, 21 Oct 2015 18:27:50 GMT</time>
</request>

浙公網安備 33010602011771號