原文: https://www.exceptionnotfound.net/writing-custom-middleware-in-asp-net-core-1-0/

Middleware是ASP.NET Core 1.0的新特性。Middleware用来检测request和response的输入输出。

什么是Middleware?

Middleware是用来检测request和response的组件。Pipeline如下:

Middleware可以用来替代HttpModules和HttpHandlers的工作。

默认的Middleware

使用VS创建的ASP.NET Core的应用,默认就使用了Middleware.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug(); if (env.IsDevelopment())
{
app.UseBrowserLink(); //Middleware
app.UseDeveloperExceptionPage(); //Middleware
}
else
{
app.UseExceptionHandler("/Home/Error"); //Middleware
} app.UseIISPlatformHandler(); //Middleware app.UseStaticFiles(); //Middleware app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
}); //Middleware
}

在上面的方法中,那些app.UserX(),就意味着使用了ASP.NET默认的Middleware组件。

自定义Middleware组件

Middleware组件和其他的class基本一样, 不同的是Middleware有一个类型为RequestDelegate的私有属性,如下:

public class AuthorizationMiddleware
{
private readonly RequestDelegate _next; public AuthorizationMiddleware(RequestDelegate next)
{
_next = next;
}
}

_next属性是一个委托,为Pipeline中下一个组件所用。 每个Middleware都实现一个async任务:

public async Task Invoke(HttpContext context)
{
await _next.Invoke(context);
}

Middleware任务

下面我们新建一个Middleware用于检查HTTP头,如果HTTP头有"X-Not-Authorized",直接返回401。代码如下:

public async Task Invoke(HttpContext context)
{
if (context.Request.Headers.Keys.Contains("X-Not-Authorized"))
{
context.Response.StatusCode = 401; //Unauthorized
return;
} await _next.Invoke(context);
}

Middleware可以做许多事。例如:

  • 你想检查每一次request,如果request请求的是一个图片,将请求redirect的一个图片handler。
  • 你想有一个组件用来记录每一次http请求
  • ..........

其他Middleware的例子

定义一个RequestHeaderMiddleware

public class RequestHeaderMiddleware
{
private readonly RequestDelegate _next; public RequestHeaderMiddleware(RequestDelegate next)
{
_next = next;
} public async Task Invoke(HttpContext context)
{
if (context.Request.Headers.Keys.Contains("X-Cancel-Request"))
{
context.Response.StatusCode = 500;
return;
} await _next.Invoke(context); if (context.Request.Headers.Keys.Contains("X-Transfer-By"))
{
context.Response.Headers.Add("X-Transfer-Success", "true");
}
}
}

这个Middleware可以做两件事情:

  • 如果HTTP请求头包含"X-Cancel-Request"那么服务器之间返回500
  • 如果HTTP请求头包含"X-Transfer-By"服务器给响应头加上"X-Transfer-Success"

定义一个ProcessingTimeMiddleware

public class ProcessingTimeMiddleware
{
private readonly RequestDelegate _next; public ProcessingTimeMiddleware(RequestDelegate next)
{
_next = next;
} public async Task Invoke(HttpContext context)
{
var watch = new Stopwatch();
watch.Start(); await _next(context); context.Response.Headers.Add("X-Processing-Time-Milliseconds", new[] { watch.ElapsedMilliseconds.ToString() });
}
}

上面的Middleware用到了Stopwatch,这个组件记录请求响应的时间,并将其作为响应头返回给客户。

添加Middeware到HTTP管道中

有两种方法将Middleware注册到pipeline中。 一种是在Startup文件的Configure方法中调用IApplicationBuilderUseMiddleware方法:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.UseMiddleware<AuthorizationMiddleware>();
...
}

另一种是我推荐使用的方法,为每个你想注册的Middleware添加一个扩展方法,然后在Configure中调用。代码如下:

public static class MiddlewareExtensions
{
public static IApplicationBuilder UseRequestHeaderMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestHeaderMiddleware>();
} public static IApplicationBuilder UseAuthorizationMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<AuthorizationMiddleware>();
} public static IApplicationBuilder UseProcessingTimeMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ProcessingTimeMiddleware>();
}
}

现在可以在Starup中使用这些扩展方法了(仔细读下面的代码,它包含一个Bug

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug(); if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
} app.UseIISPlatformHandler();
app.UseStaticFiles();
app.UseProcessingTimeMiddleware();
app.UseRequestHeaderMiddleware();
app.UseAuthorizationMiddleware(); app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}

发现这个Bug了没?

当你注册Middleware时,你添加Middleware的顺序非常重要。上面的代码中,我们首先添加了ProcessingTimeMiddleware, 然后添加RequestHeaderMiddleware,最后添加的是AuthorizationMiddleware。 这个顺序恰好搞反了。 正确的顺序如下:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.UseAuthorizationMiddleware();
app.UseRequestHeaderMiddleware();
app.UseProcessingTimeMiddleware();
...
}

总结

记住下面3点:

  • Middleware能让我们完全控制Http管道
  • Middleware可以处理验证,重定向,HTTP头,甚至取消HTTP请求
  • 在Startup中注册Middleware的顺序非常重要

本文源码-github

[译]Writing Custom Middleware in ASP.NET Core 1.0的更多相关文章

  1. [转]Writing Custom Middleware in ASP.NET Core 1.0

    本文转自:https://www.exceptionnotfound.net/writing-custom-middleware-in-asp-net-core-1-0/ One of the new ...

  2. [译]ASP.NET Core 2.0 系列文章目录

    基础篇 [译]ASP.NET Core 2.0 中间件 [译]ASP.NET Core 2.0 带初始参数的中间件 [译]ASP.NET Core 2.0 依赖注入 [译]ASP.NET Core 2 ...

  3. ASP.NET Core 3.0 自动挡换手动挡:在 Middleware 中执行 Controller Action

    最近由于发现奇怪的 System.Data.SqlClient 性能问题(详见之前的博文),被迫提前了向 .NET Core 3.0 的升级工作(3.0 Preview 5 中问题已被修复).郁闷的是 ...

  4. [译]ASP.NET Core 2.0 中间件

    问题 如何创建一个最简单的ASP.NET Core中间件? 答案 使用VS创建一个ASP.NET Core 2.0的空项目,注意Startup.cs中的Configure()方法: public vo ...

  5. [译]ASP.NET Core 2.0 带初始参数的中间件

    问题 如何在ASP.NET Core 2.0向中间件传入初始参数? 答案 在一个空项目中,创建一个POCO(Plain Old CLR Object)来保存中间件所需的参数: public class ...

  6. [译]ASP.NET Core 2.0 全局配置项

    问题 如何在 ASP.NET Core 2.0 应用程序中读取全局配置项? 答案 首先新建一个空项目,并添加两个配置文件: 1. appsettings.json { "Section1&q ...

  7. [译]ASP.NET Core 2.0 机密配置项

    问题 如何在ASP.NET Core 2.0中保存机密配置项(不用将其暴露给源代码管理器)? 答案 创建一个ASP.NET Core 2.0空项目,在项目节点上点击右键,并点击菜单项 - 管理用户机密 ...

  8. [译]ASP.NET Core 2.0 会话状态

    问题 如何在ASP.NET Core 2.0中存储会话状态? 答案 创建一个空项目,修改Startup类的ConfigureServices()方法,添加会话状态服务和它后台的存储服务: public ...

  9. [译]ASP.NET Core 2.0 本地文件操作

    问题 如何在ASP.NET Core 2.0中受限地访问本地目录和文件信息? 答案 新建一个空项目,修改Startup类,添加访问本地文件所需的服务: public void ConfigureSer ...

随机推荐

  1. ubuntu kylin 14.04安装搜狗输入法

    1.卸载原有的输入法,fcitx或ibus.如卸载fcitx: sudo apt-get remove fcitx*(如不需保留配置文件用purge) sudo apt-get autoremove( ...

  2. C语言调试过程中duplicate symbol错误分析

    说明:在我们调试C语言的过程中,经常会遇到duplicate symbol错误(在Mac平台下利用Xcode集成开发环境).如下图: 一.简单分析一下C语言程序的开发步骤. 由上图我们可以看出C语言由 ...

  3. SCNU 2015ACM新生赛初赛【1001~1011】个人解题思路

            题目1001:       大意:已知$n$个角色,$m$种怪物种族,$k$个怪物,给出一组角色编号,编号$P_{i}$的角色能肝死编号$i$的怪物,对于给定的一组怪物编号,为了打通关 ...

  4. Yahoo14条军规-前端性能优化

    1.尽可能减少HTTP请求数 什么是http请求? 2.使用CDN(内容分发网络) 什么是CDN? 3.添加Expire/Cache-Control头 Expire Cache-Control 4.启 ...

  5. 关于如何使用sourcetree将本地项目提交到远端github总结?

    使用sourcetree将本地项目提交到github里,目前来说还是很流行的,我也是听说好玩,所以来琢磨了一下,从环境搭建到配置好,差不多用了一下午加一晚上的时间,有点虐心,好吧,废话不多说,介绍一下 ...

  6. jmeter(九)逻辑控制器

    jmeter中逻辑控制器(Logic Controllers)的作用域只对其子节点的sampler有效,作用是控制采样器的执行顺序. jmeter提供了17种逻辑控制器,它们各个功能都不尽相同,大概可 ...

  7. 对应sslocal的简易luci web界面

    直接通过官方ss代码库安装的ss, 只有一个可执行的sslocal, 也没找到对应的luci-app, 所以就自己写了一个. 因为/etc/init.d/sslocal 和 /etc/config/s ...

  8. 如何查看当前Ubuntu系统的版本

    如何查看当前Ubuntu系统的版本 说来也惭愧,用Ubuntu差不多快1个月了,双系统是让朋友安的,只知道自己使用的是什么12版本的,具体怎么看还不知道,下面写一下查看当前Linux系统的版本的方法 ...

  9. [LeetCode] Jump Game II 跳跃游戏之二

    Given an array of non-negative integers, you are initially positioned at the first index of the arra ...

  10. logback 常用配置详解<appender>

    logback 常用配置详解 <appender> <appender>: <appender>是<configuration>的子节点,是负责写日志的 ...