dotNET.NETC#

使用 Nancy 开发 selfhost 和 IIS host

2018-05-07  本文已影响123人  天兵公园

早几年用 C# 开发一个小型站点或者一个简单的服务模块,还得开 Visual Studio 写一个 WebForm 或者 MVC 站点,想写个服务还得用 WCF,太麻烦了,最近,从前年开始,我就开始将一些 APP 的服务端从 WCF 改成 用 Nancyfx 作为框架开发的独立运行的 “微服务” 模式了。

Nancy 其实很早就出来了,无论在 .NET 还是 Mono 还是目前的 .NET Core 下都可以跑起来,我目前的做法是写好业务层,然后就可以写一个 selfhost 的独立运行程序,将它打包为一个 EXE 就可以运行起来,或者丢到 IIS 里也可以运行起来,不管在 Windows 下还是 Linux 还是 MacOS 下,程序永远都可以运行,不需要修改一行代码,我觉得这 TM 才是真正的跨平台吧。

Nancy
https://github.com/NancyFx/Nancy

不过我还在使用 1.x 的版本,如要使用旧版本,可以切换 1.x 的分支去看对应的示例,不过要使用的话还是使用最新的版本吧。

这是官方的一个 Hello World 栗子。

public class Module : NancyModule
{
    public Module()
    {
        Get("/greet/{name}", x => {
            return string.Concat("Hello ", x.name);
        });
    }
}

我的项目中,将所有的业务通过 Module 区分,由于只有少量的 JS / CSS /Html 资源,以及不打算使用 Razor 引擎,所有所有的资源也全部封装到一个资源 DLL 里了,内嵌资源的加载方式很简单:

 var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Hello.html");

我觉得在不太需要复杂的页面渲染逻辑的项目中,使用 DotLiquid 要简便得多,怎么引用,可以上 Nuget.org 搜索,然后通过 PM 导入即可,我把模板的渲染作为一个 String 类型的扩展方法,更加方便使用。

public static string AsHtmlFromTemplate(this string tmpl, object model)
{
    return Template.Parse(tmpl).Render(Hash.FromAnonymousObject(model));
}

想要调用的话可以一行代码就可以:

htmlTemplate.AsHtmlFromTemplate(new { Title = "Hello" });

通常 Response 输出或响应的类型有很多,文本,二进制文件,流,其他响应,以及会在 Header 或 Cookie 写入什么值,这些用的都很普遍,看看 Nancy 是怎么处理这些类型的输出。

文本类型最简单,通过在 Response 对象上调用 AsText 即可。

Response.AsText("内容","MIME") 

JSON 类型,可以使用 AsJson 方法,传递匿名对象就可以达到目的。

对于流输出,需要构造 Nancy 的 Stream ,比如输出字体文件,可以这么做:

Nancy.Responses.StreamResponse streamResponse = new Nancy.Responses.StreamResponse(() => { return fontstream; }, mime);

要设置 Header,只需要在 StreamResponse 的调用 WithHeader 即可。

不过在 IIS Host 的 ASP.NET 中使用路径和 SelfHost 模式有些不同,在 ASP.NET 通常我们获取路径的方式是 ~/YourPath ,而在 SelfHost 中是直接访问文件系统的绝对路径或相对路径。我这里做法是,在业务模块中,创建一个接口,比如 IUnc,然后 SelfHost 或 ASP.NET 中分别实现他们,告诉根路径的物理上的绝对路径。

public interface IUnc
{
    string GetRoot();
}

以及他们分别在 ASP.NET 或 Console Client 中的实现:

public string GetRoot()
{
    return HttpContext.Current.Server.MapPath("~/");
}
public string GetRoot()
{
    return Environment.CurrentDirectory;
}

然后通过反射,获取 IUNC 的实现类,确定 Root 路径的地址:

private void DetectRoot()
{
    string root = Environment.CurrentDirectory;

    try
    {
        var types = AppDomain.CurrentDomain.GetAssemblies()
                 .SelectMany(a => a.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(IUnc))))
                 .ToArray();
        foreach (var v in types)
        {
            var msds = v.GetMethods();
            foreach (var msd in msds)
            {
                root = (Activator.CreateInstance(v) as IUnc).GetRoot();
                break;
            }
            break;
        }
    }
    catch(Exception ex)
    {

    }
    _root = root;
}

在 Nancy 中,如果需要对 "模块" 进行授权,例如 Basic 认证,Form 认证,可以在每个 Module 的构造方法中调用 RequiresAuthentication。

this.RequiresAuthentication();

以上就是在我使用的过程中,遇到的一些简单的使用的方法,其实写到最后,项目结构都比较复杂了,比如光 Nancy 的 Bootstraper 类都达到了数百行,随着业务增多,复杂化,逻辑也变得很不清晰,但是已经上线的项目中,我用它也完成了跨 Linux 和 Windows 的两个平台部署,以及在 Windows 上也实现了 Nginx+SelfHost 模式承载服务和 IIS 承载模式。

如果你需要一个轻量的微服务架构,像 Springboot 那样,不妨考虑试试 Nancy :)

上一篇下一篇

猜你喜欢

热点阅读