📜 ⬆️ ⬇️

Preparing ASP.NET Core: let's talk more about OWIN and Katana

We are happy to share with you another article from the series of articles on the ASP.NET Core platform (formerly ASP.NET 5). This time Vyacheslav Bobik, a .NET developer from Radario, will continue his story about the platform with a story about the use of OWIN, Katana technologies and related issues. All articles of the series you can always find here #aspnetcolumn - Vladimir Yunev

A bit of history


In the distant past, when the ASP.NET MVC version of CTP only appeared, no one thought about cross-platform, that it would be great to run applications written on this framework, not only on IIS, but also on another web server, and on another OS.
Over time, the richness of the ASP.NET MVC framework has grown, and the monolithic System.Web library has grown, on which the framework is built and its complexity increases. At some point, namely the fourth version, this framework became quite large, almost nailed to the IIS.

On the other hand, there was ASP.NET Web API, which had no direct dependencies on the IIS event model, and could be hosted independently without IIS (self-hosting). At some point, an understanding came to the guys from Microsoft that we needed an instument allowing the ability to run web applications written in .Net not only on IIS, but also on other web servers, as well as provide flexibility in the processing requests. In summary, the OWIN specification and the Katana project appeared.

Getting started with OWIN


Katana is a set of components for creating and running web applications for hosting abstraction. Hosting abstraction is OWIN. The main interface in OWIN is called application delegate or AppFunc .

using AppFunc = Func<IDictionary<string, object>, Task>; 

Each OWIN application must have a Startup class in which the definition of our components will be located. There are several ways to report this class to our application.
')
The first is to create a Startup class in our application with the Configuration method, which accepts IAppBuilder

 public class Startup { public void Configuration(IAppBuilder app) { app.Use(...); app.Use(...); } } 

With the help of such calls app.Use (..) we can flexibly configure our request processing process (pipeline).

Second - mark with a special attribute:

 [assembly: OwinStartup(typeof(App.TestApp))] 

In ASP.NET Core, OWIN support is based on the Katana project, with changes and additions. So IAppBuilder was replaced by IApplicationBuilder , but if you worked with Katana you won’t be hard pressed to write your OWIN module.

Let's, as is customary all over the world, write a simple hello world module. Create an empty ASP.NET Core project. Your Startup class should look something like this:

 public class Startup { public void ConfigureServices(IServiceCollection services) { } public void Configure(IApplicationBuilder app) { // Add the platform handler to the request pipeline. app.UseIISPlatformHandler(); app.Run(async (context) => { await context.Response.WriteAsync("Hello World!"); }); } } 

If your class looks a little different, it's okay, in the example I'm using ASP.NET 5 beta-8 (the current version at the time of this writing is WY ), in earlier versions of Startup we can look a bit different. Also install the nuget package Microsoft.AspNet.Owin. To do this, let's go into project.json and insert it in the dependencies section.

 Microsoft.AspNet.Owin" : "1.0.0-beta8" 

Now for an example we will write a small method:

 public Task OwinHello(IDictionary<string, object> enviroment) { var responseText = "Hello via Owin"; var responseBytes = Encoding.UTF8.GetBytes(responseText); var responseStream = (Stream)enviroment["owin.ResponseBody"]; var responseHeaders = (IDictionary<string, string[]>)enviroment["owin.ResponseHeaders"]; responseHeaders["Content-Length"] = new string[] { responseBytes.Length.ToString(CultureInfo.InvariantCulture) }; responseHeaders["Content-Type"] = new string[] { "text/plain" }; return responseStream.WriteAsync(responseBytes, 0, responseBytes.Length); } 

and add a call to our OwinHello method to the Configure method.

 app.UseOwin(pipeline => pipeline(next => OwinHello)); 

Now our Startup class will look like this:

 public class Startup { public void ConfigureServices(IServiceCollection services) { } public void Configure(IApplicationBuilder app) { // Add the platform handler to the request pipeline. app.UseIISPlatformHandler(); app.UseOwin(pipeline => pipeline(next => OwinHello)); app.Run(async (context) => { await context.Response.WriteAsync("Hello World"); }); } public Task OwinHello(IDictionary<string, object> enviroment) { var responseText = "Hello Owin"; var responseBytes = Encoding.UTF8.GetBytes(responseText); var responseStream = (Stream)enviroment["owin.ResponseBody"]; var responseHeaders = (IDictionary<string, string[]>)enviroment["owin.ResponseHeaders"]; responseHeaders["Content-Length"] = new string[] { responseBytes.Length.ToString(CultureInfo.InvariantCulture) }; responseHeaders["Content-Type"] = new string[] { "text/plain" }; return responseStream.WriteAsync(responseBytes, 0, responseBytes.Length); } } 

If you now run the project, the browser will be "Hello Owin". Perhaps the reader may be asked: "Why was Hellow Owin times out without Hello World?". This is because, according to the specification , changes of headers, status of the code, request body, etc., are possible only before the first entry into the request body (response body stream).

To access the internals of the request, I use the keys owin.ResponseBody and owin.ResponseHeaders . The entire set of keys can also be viewed in the specification . We dealt with the execution of the request, but we have two different methods, for using owin, Run and Use , which, at first glance, do the same thing. This is not entirely true.

Run - the agreements are described in such a way that this method should be used only when we want to add our middleware to the end of the processing request (pipeline), respectively, after Run nothing will be called.

Let's write something more complicated now, for example a component with a simple basic authorization. Guys from the ASP.NET team for more the best difficult cases, it is recommended to design middleware separate class, and to make calls to your components as an extension method.

Create a BasicAuthMiddleware class with a single Invoke method.

 //     production public class BasicAuthMiddleware { private readonly RequestDelegate _next; public BasicAuthMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { if (context.Request.Headers["Authorization"] == "Basic aGFicmE6aGFicg==") { context.Response.StatusCode = 200; await _next.Invoke(context); } else { context.Response.StatusCode = 401; context.Response.Headers.Add("WWW-Authenticate", "Basic realm=\"localhost\""); } } } 

Now we will write a simple extension method. Create a BasicAuthMiddlewareExtension class

 public static class BasicAuthMiddlewareExtension { public static IApplicationBuilder UseBasicAuth(this IApplicationBuilder builder) { return builder.UseMiddleware<BasicAuthMiddleware>(); } } 

Let's connect our component in the Startup class:

 public class Startup { public void ConfigureServices(IServiceCollection services) { } public void Configure(IApplicationBuilder app) { app.UseIISPlatformHandler(); app.UseBasicAuth(); app.Run(async (context) => { await context.Response.WriteAsync("Hello World"); }); } } 

Now, if we run our project, then we should see a window for entering a login and password:

If we enter the correct login - habra, password - habr, then we will see Hello World . So, it’s not hard enough to write your middleware component.

Conclusion


In this article, we learned the basics of OWIN. You saw how easy it is to connect and create your components, embed them into the pipeline of the request, and also wrote your module following the recommendations of Microsoft.

To authors


Friends, if you are interested in supporting the column with your own material, please write to me at vyunev@microsoft.com to discuss all the details. We are looking for authors who can interestingly tell about ASP.NET and other topics.

about the author


Bobik Vyacheslav Borisovich,
.NET Developer at Radario

A young .Net programmer with 3 years of experience. ASP.NET MVC developer, author of Windows and Windows phone applications.

Source: https://habr.com/ru/post/275817/


All Articles