ASP.NET 5 Middleware, Or Where Has My HttpModule Gone?
31 March 2015 13:22
ASP.NET 5 has been largely rewritten from the ground up, and incorporates some radical changes when compared with previous versions of ASP.NET. One of the biggest changes is in the HTTP Pipeline. This article looks at how those changes impact the design and registration of plug and play components that used to be represented by HttpModules.
HttpModules act as request filters in ASP.NET versions prior to 5. They are reusable units of code that can be plugged into the request pipeline, and tasked with responding to events defined in the HttpApplication
class as they are fired. The kind of things that modules are used for include managing authentication, global error handling and logging. Traditional forms authentication is implemented as a module, and I have written about using ELMAH, a component that logs details of unhandled exceptions, which is impemented as an HttpModule. Modules can also be used to intercept and modify the outbound response to remove white space or compress it, for example. Modules implement the IHttpModule
interface, which is defined in System.Web
, and that library is not part of the new ASP.NET.
HttpModule code can either be added to the relevant event handler in Global.asax, or more usually, created as class libraries and registered with the application in the web.config file.
What Is Middleware?
The definition of "Middleware" varies widely depending on its context, but in relation to ASP.NET 5, the definition provided by the OWIN specification is probably closest:
Pass through components that form a pipeline between a server and application to inspect, route, or modify request and response messages for a specific purpose.
That's also pretty much the description of traditional HttpModules and their kin, HttpHandlers.
Access to the request pipeline in an ASP.NET 5 application is provided in the Startup
class (Startup.cs file), which is the entry point to the application itself. The Startup
class includes a Configure
method which takes anIApplicationBuilder
type as a parameter. The IApplicationBuilder
interface, an instance of which is represented by the parameter app
, provides a number of extension methods by which components can be plugged in to the request pipeline. The major difference between this and the previous implementation of the HTTP pipeline is that the event-driven approach has been replaced with a composable model. In the new ASP.NET, the order in which components are added is important. The Beta 3.0 MVC Starter site template has some components added with comments that explain their purpose:
// Add static files to the request pipeline.
app.UseStaticFiles(); // Add cookie-based authentication to the request pipeline.
app.UseIdentity(); // Add MVC to the request pipeline.
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" }); // Uncomment the following line to add a route for porting Web API 2 controllers.
// routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");
});
The methods by which Identity, MVC and static files management are added to the pipeline are extension methods on the IApplicationBuilder
type. When adding the middleware that will be built in this article, I will also create an extension method for adding it to the pipeline.
The Example Middleware
The example middleware that I shall create will do two things: it will measure the time taken to process the request and it will add the value to both the outgoing html and as a custom response header. That might be three things. Anyhoo, the example will feature two of the most common scenarios illustrated in the plethora of existing "Build your own HttpModule" tutorials on the Internet - modifying the response output and adding headers. The code for the middleware class is as follows:
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks; namespace WebApplication1.MiddleWare
{
public class MyMiddleware
{
RequestDelegate _next; public MyMiddleware(RequestDelegate next)
{
_next = next;
} public async Task Invoke(HttpContext context)
{ var sw = new Stopwatch();
sw.Start(); using (var memoryStream = new MemoryStream())
{
var bodyStream = context.Response.Body;
context.Response.Body = memoryStream; await _next(context); var isHtml = context.Response.ContentType?.ToLower().Contains("text/html");
if (context.Response.StatusCode == 200 && isHtml.GetValueOrDefault())
{
{
memoryStream.Seek(0, SeekOrigin.Begin);
using (var streamReader = new StreamReader(memoryStream))
{
var responseBody = await streamReader.ReadToEndAsync();
var newFooter = @"<footer><div id=""process"">Page processed in {0} milliseconds.</div>";
responseBody = responseBody.Replace("<footer>", string.Format(newFooter, sw.ElapsedMilliseconds));
context.Response.Headers.Add("X-ElapsedTime", new[] { sw.ElapsedMilliseconds.ToString() });
using (var amendedBody = new MemoryStream())
using (var streamWriter = new StreamWriter(amendedBody))
{
streamWriter.Write(responseBody);
amendedBody.Seek(0, SeekOrigin.Begin);
await amendedBody.CopyToAsync(bodyStream);
}
}
}
}
}
}
}
}
A private field of type RequestDelegate
is declared. This is used to store the RequestDelegate
that is passed in to the constructor that takes a RequestDelegate
. A RequestDelegate
is a function that takes an HttpContext
instance and returns a Task
:
public delegate Task RequestDelegate(HttpContext context);
The HttpContext
object is similar to the one found in previous versions of ASP.NET in that it provdes access to the request and response etc, but it is a different animal altogether and much, much slimmer.
In ASP.NET 5, middleware is an instance of Func<RequestDelegate, RequestDelegate>
- a delegate that takes aRequestDelegate
as a parameter and returns a RequestDelegate
. And as described above, the RequestDelegate
is a function that returns a function, and it is in this way that the pipeline is built, with each piece of middleware being chained to the next and responsible for passing the processing on to the next in line (if appropriate).
The Invoke
method is a convention-based method in that the runtime looks for a method with that name and will call it. It is where the action happens within your middleware, and it is where you pass control on to the next component in the pipeline if appropriate. Sometimes you won't want to pass control on - you will want to halt execution of the pipeline altogether, if, for example, you are writing a custom authentication middleware that determines that the current user should proceed no further. Control is passed to the next component by callingawait _next(context)
. Code that you place before this call is executed, and in this example that code creates aStopwatch
instance and starts it. The code also gets a reference to the response content, which is implemented as a stream. Then the next component is called, which in turn calls the next component and so on. Then control is passed back up the chain of components and any code that has been added after the await _next(context)
call is executed. It is in that block of code in this example that the response body is modified to include some HTML to display the elapsed time in milliseconds somewhere in the footer region of the page. At the same time, a custom header is added with the elapsed time as its value.
Plugging into the pipeline
The next step is to plug the component into the pipeline. As I mentioned earlier, this is achieved via an extension method on the IApplicationBuilder
type:
using Microsoft.AspNet.Builder; namespace ASPNET5Test.MiddleWare
{
public static class BuilderExtensions
{
public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder app)
{
return app.UseMiddleware<MyMiddleware>();
}
}
}
The result is a wrapper around the existing UseMiddleware<T>
extension method (currently found in theMicrosoft.AspNet.Builder
namespace - hence the using
directive) into something a little prettier.
Finally, the extension method is called in the Configure
method of the Startup
class:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory)
{
app.UseMyMiddleware(); // etc
When the site is run for the first time, the processing time takes a while - just under two seconds in this image - and is printed to the bottom of the screen (with a little CSS assistance):
And the developer tools in Chrome provide an easy way to confirm that the custom header was added:
Summary
In this article, I introduced the ASP.NET5 replacement to traditional HttpModules and showed how to create one and plug it in to the application pipeline. This article is based on Beta 3 of ASP.NET 5 and the concepts illustrated here are subject to change. I will endeavour to keep the article current in line with future releases.
ASP.NET 5 Middleware, Or Where Has My HttpModule Gone?的更多相关文章
- ASP.NET Core Middleware 抽丝剥茧
一. 宏观概念 ASP.NET Core Middleware是在应用程序处理管道pipeline中用于处理请求和操作响应的组件. 每个组件是pipeline 中的一环. 自行决定是否将请求传递给下一 ...
- ASP.NET Core Middleware (转载)
What is Middleware? Put simply, you use middleware components to compose the functionality of your A ...
- Prerender Application Level Middleware - ASP.NET Core Middleware
In the previous post Use Prerender to improve AngularJS SEO, I have explained different solutions at ...
- [ASP.NET Core] Middleware
前言 本篇文章介绍ASP.NET Core里,用来处理HTTP封包的Middleware,为自己留个纪录也希望能帮助到有需要的开发人员. ASP.NET Core官网 结构 在ASP.NET Core ...
- ASP.NET Core Middleware管道介绍
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.Use(async (context, ne ...
- ASP.NET WebAPI框架解析第二篇(HttpModule的创建和使用)
我们先看一下执行流程图 图中画红圈的部分便是HttpModule,在说创建HttpModule之前,先说一下HttpApplication对象,HttpApplication对象由Asp.net框架创 ...
- ASP.NET Core中Middleware的使用
https://www.cnblogs.com/shenba/p/6361311.html ASP.NET 5中Middleware的基本用法 在ASP.NET 5里面引入了OWIN的概念,大致意 ...
- Prerender Application Level Middleware - ASP.NET HttpModule
In the previous post Use Prerender to improve AngularJS SEO, I have explained different solutions at ...
- 翻译 - ASP.NET Core 基本知识 - 中间件(Middleware)
翻译自 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-5.0 中间件是集成 ...
随机推荐
- https配置指导
为了使Apache支持https访问,系统需要安有apache.openssl.mod_ssl.so 证书申请 https://ninghao.net/blog/4449 安装证书时 安装编译open ...
- 20位活跃在Github上的国内技术大牛
登录|注册 leon-这个程序员不闷骚的博客 喜欢leon,有追求有原则有爱心的杀手,做一个有追求的程序员,代码是程序员的朋友,虽然没有热情,但是非常忠实.希望拥有一身绝世武功,再配一把绝世好 ...
- Cassandra学习六 一些知识点
http://www.flyml.net/2016/09/08/cassandra-tutorial-java-api-example/ Cassandra对查询的支持很弱,只支持主键列及索引列的查询 ...
- python学习(三) 使用字符串
第三章 使用字符串 ...
- 实验吧CTF题库-WEB题(部分)
看起来有点难 提交admin http://ctf5.shiyanbar.com/basic/inject/index.php?admin=admin&pass=admin&actio ...
- 玩转angularJs——通过自定义ng-model,不仅仅只是input可以实现双向数据绑定
体验更优排版请移步原文:http://blog.kwin.wang/programming/angularJs-user-defined-ngmodel.html angularJs双向绑定特性在开发 ...
- wordpress 基础文件
需要用到的PHP基础文件有: 404.php 404模板 rtl.css 如果网站的阅读方向是自右向左的,会被自动包含进来 comments.php 评论模板 single.php 文章模板.显 ...
- 在线HTTP POST/GET接口测试工具
在线HTTP POST/GET接口测试工具 http://www.atool.org/httptest.php
- 【283】ArcMap 中河流字体设置
左斜字体的设置 1. 右键属性设置如下,将字体角度如下设置,并点击改变样式的按钮 2. 首先设置颜色如下,然后设置加粗斜体,最后勾选 CJK character orientation 的复选框 C ...
- 【原】Coursera—Andrew Ng机器学习—课程笔记 Lecture 8_Neural Networks Representation 神经网络的表述
神经网络是一种受大脑工作原理启发的模式. 它在许多应用中广泛使用:当您的手机解释并理解您的语音命令时,很可能是神经网络正在帮助理解您的语音; 当您兑现支票时,自动读取数字的机器也使用神经网络. 8.1 ...