{"id":1505,"date":"2020-04-20T10:00:44","date_gmt":"2020-04-20T08:00:44","guid":{"rendered":"http:\/\/tpodolak.com\/blog\/?p=1505"},"modified":"2020-04-19T00:43:25","modified_gmt":"2020-04-18T22:43:25","slug":"running-asp-net-core-together-nancy","status":"publish","type":"post","link":"https:\/\/tpodolak.com\/blog\/2020\/04\/20\/running-asp-net-core-together-nancy\/","title":{"rendered":"Running ASP.NET Core together with Nancy"},"content":{"rendered":"<h3>1. Introduction<\/h3>\n<p>Our current <i>API<\/i> runs on <a href=\"https:\/\/github.com\/NancyFx\/Nancy\">Nancy<\/a> which in my opinion is past its prime. Recent news from the <a href=\"https:\/\/github.com\/NancyFx\/Nancy\/issues\/3010\">GitHub<\/a> issue tracker seems to confirm that thesis, that is why we started looking for a migration path from Nancy based <i>API<\/i> to <i>ASP.NET Core<\/i>. Because the codebase is quite large we didn&#8217;t want to do a <i>Big Bang Rewrite<\/i> but instead of that, we wanted to gradually replace old <i>API<\/i> with a new one, so both of them can coexist next to each other.<\/p>\n<h3>2. Legacy API<\/h3>\n<p>Before I jump into implementation, here is a sample <I>Nancy API<\/i> with two endpoints &#8211; <i>products<\/i> and <i>variants<\/i><\/p>\n<pre lang=csharp>\r\npublic class Startup\r\n{\r\n    \/\/ This method gets called by the runtime. Use this method to add services to the container.\r\n    public void ConfigureServices(IServiceCollection services)\r\n    {\r\n        \/\/ nancy has its own container\r\n    }\r\n\r\n    \/\/ This method gets called by the runtime. Use this method to configure the HTTP request pipeline.\r\n    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)\r\n    {\r\n        app.UseOwin(owin => owin.UseNancy());\r\n    }\r\n}\r\n<\/pre>\n<pre lang=csharp>\r\npublic sealed class ProductsNancyModule : NancyModule\r\n{\r\n    public ProductsNancyModule()\r\n    {\r\n        Get(\"products\", args => Response.AsJson(new List<Product>\r\n        {\r\n            new Product(Guid.NewGuid().ToString(), \"First product from nancy\"),\r\n            new Product(Guid.NewGuid().ToString(), \"Second product from nancy\")\r\n        }));\r\n    }\r\n}\r\n<\/pre>\n<pre lang=csharp>\r\npublic sealed class VariantsNancyModule : NancyModule\r\n{\r\n    public VariantsNancyModule()\r\n    {\r\n        Get(\"variants\", args => Response.AsJson(new List<Variant>\r\n        {\r\n            new Variant(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), \"first variant from nancy\"),\r\n            new Variant(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), \"second variant from nancy\")\r\n        }));\r\n    }\r\n}\r\n<\/pre>\n<p>The goal is to replace <i>products<\/i> endpoint with <i>ASP.NET Core<\/i> implementation while <i>variants<\/i> endpoint should still be served by <i>Nancy<\/i><\/p>\n<h3>3. Combining Nancy with <i>ASP.NET Core<\/i><\/h3>\n<p>The solution is based on <a href=\"https:\/\/docs.microsoft.com\/en-us\/aspnet\/core\/fundamentals\/middleware\/?view=aspnetcore-3.1#branch-the-middleware-pipeline\">branching<\/a> the pipeline feature which is available starting from <i>ASP.NET Core<\/i> 2.1. Long story short &#8211; it is possible to configure different pipelines for different route paths thanks to<\/p>\n<pre lang=csharp>\r\npublic static IApplicationBuilder Map(this IApplicationBuilder app, PathString pathMatch, Action<IApplicationBuilder> configuration)\r\n<\/pre>\n<p>method. Having that in mind we can `Map` <i>products<\/i> path to run through <i>ASP.NET Core<\/i> pipeline whereas the rest would go through <i>Nancy<\/i>.<\/p>\n<pre lang=csharp>\r\n\/\/ This method gets called by the runtime. Use this method to add services to the container.\r\npublic void ConfigureServices(IServiceCollection services)\r\n{\r\n    \/\/ register mvc services\r\n    services.AddControllers();\r\n    services.AddMvc(options => options.EnableEndpointRouting = false);\r\n}\r\n\r\n\/\/ This method gets called by the runtime. Use this method to configure the HTTP request pipeline.\r\npublic void Configure(IApplicationBuilder app, IWebHostEnvironment env)\r\n{\r\n    \/\/ run everything with products prefix through mvc\r\n    app.Map(\"\/products\", appBuilder =>\r\n    {\r\n        appBuilder.UseMvc();\r\n    });\r\n\r\n    \/\/ run the rest through Nancy\r\n    app.Map(string.Empty, appBuilder =>\r\n    {\r\n        appBuilder.UseOwin(options => options.UseNancy());\r\n    });\r\n}\r\n<\/pre>\n<pre lang=csharp>\r\n[ApiController]\r\npublic class ProductsController : ControllerBase\r\n{\r\n    [HttpGet(\"\/products\")]\r\n    public List<Product> Get()\r\n    {\r\n        return new List<Product>\r\n        {\r\n            new Product(Guid.NewGuid().ToString(), \"First product from asp net core\"),\r\n            new Product(Guid.NewGuid().ToString(), \"Second product from asp net core\")\r\n        };\r\n    }\r\n}\r\n<\/pre>\n<p>At this point we are almost there, however running request through <i>Map<\/i> pipeline will remove the path prefix, meaning that our controller would be accessible under <i>\/<\/i> path instead of <i>products<\/i>. In order to bypass this limitation we have to restore original prefix with <i>RewriteMiddleware<\/i>. Once we put it all together, now we are able to replace multiple <i>Nancy<\/i> endpoints with <i>ASP.NET Core<\/i> ones using following piece of code<\/p>\n<pre lang=\"csharp\">\r\n\/\/ This method gets called by the runtime. Use this method to configure the HTTP request pipeline.\r\npublic void Configure(IApplicationBuilder app, IWebHostEnvironment env)\r\n{\r\n    var aspNetCorePaths = new PathString[] {\"\/products\"};\r\n    foreach (var path in aspNetCorePaths)\r\n    {\r\n        app.Map(path, appBuilder =>\r\n        {\r\n            \/\/ we still want to access the resource under old url, so we need to add the branch path back to the route\r\n            var rewriteOptions = new RewriteOptions().AddRewrite(\r\n                \"(?<Path>.*)\",\r\n                $\"{path}\/${{Path}}\",\r\n                skipRemainingRules: true);\r\n            appBuilder.UseRewriter(rewriteOptions);\r\n            appBuilder.UseMvc();\r\n        });\r\n    }\r\n\r\n    app.Map(string.Empty, appBuilder =>\r\n    {\r\n        appBuilder.UseOwin(options => options.UseNancy());\r\n    });\r\n}\r\n<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"\/\/tpodolak.com\/blog\/wp-content\/uploads\/2020\/04\/running-asp-net-core-together-with-nancy\/NancyCore.gif\" alt=\"\" width=\"1030\" height=\"758\" class=\"alignnone size-full wp-image-1517\" \/><br \/>\nSource code for this post can be found <a href=\"https:\/\/github.com\/tpodolak\/Blog\/tree\/master\/RunningAspNetCoreTogetherWithNancy\">here<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>1. Introduction Our current API runs on Nancy which in my opinion is past its prime. Recent news from the GitHub issue tracker seems to confirm that thesis, that is why we started looking for a migration path from Nancy based API to ASP.NET Core. Because the codebase is quite large we didn&#8217;t want to [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[317],"tags":[318,341],"class_list":["post-1505","post","type-post","status-publish","format-standard","hentry","category-asp-net-core","tag-asp-net-core","tag-nancy"],"_links":{"self":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/1505","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/comments?post=1505"}],"version-history":[{"count":15,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/1505\/revisions"}],"predecessor-version":[{"id":1521,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/1505\/revisions\/1521"}],"wp:attachment":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/media?parent=1505"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/categories?post=1505"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/tags?post=1505"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}