使用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>