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

参考:

https://liulixiang1988.github.io/2014/09/12/Nancy框架/

posted @ 2023-09-14 21:38  harrychinese  阅读(202)  评论(0编辑  收藏  举报