- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章ASP.NET Core处理管道的深入理解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
在 asp.net core 的管道处理部分,实现思想已经不是传统的面向对象模式,而是切换到了函数式编程模式。这导致代码的逻辑大大简化,但是,对于熟悉面向对象编程,而不是函数式编程思路的开发者来说,是一个比较大的挑战.
在 asp.net core 中,一次请求的完整表示是通过一个 httpcontext 对象来完成的,通过其 request 属性可以获取当前请求的全部信息,通过 response 可以获取对响应内容进行设置.
对于一次请求的处理可以看成一个函数,函数的处理参数就是这个 httpcontext 对象,处理的结果并不是输出结果,结果是通过 response 来完成的,从程序调度的角度来看,函数的输出结果是一个任务 task.
这样的话,具体处理 http 请求的函数可以使用如下的 requestdelegate 委托进行定义.
1
|
public
delegate
task requestdelegate(httpcontext context);
|
在函数参数 httpcontext 中则提供了此次请求的所有信息,context 的 request 属性中提供了所有关于该次请求的信息,而处理的结果则在 context 的 response 中表示。通常我们会修改 response 的响应头,或者响应内容来表达处理的结果.
需要注意的是,该函数的返回结果是一个 task,表示异步处理,而不是真正处理的结果.
参见:在 doc 中查看 requestdelegate 定义 。
我们从 asp.net core 的源代码中选取一段作为参考,这就是在没有我们自定义的处理时,asp.net core 最终的处理方式,返回 404。这里使用函数式定义.
1
2
3
4
5
6
7
|
requestdelegate app = context =>
{
// ......
context.response.statuscode = statuscodes.status404notfound;
return
task.completedtask;
};
|
来源:在 github 中查看 applicationbuilder 源码 。
把它翻译成熟悉的方法形式,就是下面这个样子:
1
2
3
4
5
6
7
|
public
task app(httpcontext context)
{
// ......
context.response.statuscode = statuscodes.status404notfound;
return
task.completedtask;
};
|
这段代码只是设置了 http 的响应状态码为 404,并直接返回了一个已经完成的任务对象.
为了脱离 asp.net core 复杂的环境,可以简单地进行后继的演示,我们自定义一个模拟 httpcontext 的类型 httpcontextsample 和相应的 requestdelegate 委托类型.
在模拟请求的 httpcontextsample 中,我们内部定义了一个 stringbuilder 来保存处理的结果,以便进行检查。其中的 output 用来模拟 response 来处理输出.
而 requestdelegate 则需要支持现在的 httpcontextsample.
1
2
3
4
5
6
7
8
9
10
11
|
using
system.threading.tasks;
using
system.text;
public
class
httpcontextsample
{
public
stringbuilder output {
get
;
set
; }
public
httpcontextsample() {
output =
new
stringbuilder();
}
}
public
delegate
task requestdelegate(httpcontextsample context);
|
这样,我们可以定义一个基础的,使用 requestdelegate 的示例代码.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// 定义一个表示处理请求的委托对象
requestdelegate app = context =>
{
context.output.appendline(
"end of output."
);
return
task.completedtask;
};
// 创建模拟当前请求的对象
var context1 =
new
httpcontextsample();
// 处理请求
app(context1);
// 输出请求的处理结果
console.writeline(context1.output.tostring());
|
执行之后,可以得到如下的输出 。
end of output. 。
处理管道中间件 。
所谓的处理管道是使用多个中间件串联起来实现的。每个中间件当然需要提供处理请求的 requestdelegate 支持。在请求处理管道中,通常会有多个中间件串联起来,构成处理管道.
但是,如何将多个中间件串联起来呢?
可以考虑两种实现方式:函数式和方法式.
方法式就是再通过另外的方法将注册的中间件组织起来,构建一个处理管道,以后通过调用该方法来实现管道。而函数式是将整个处理管道看成一个高阶函数,以后通过调用该函数来实现管道.
方法式的问题是在后继中间件处理之前需要一个方法,后继中间件处理之后需要一个方法,这就是为什么 asp.net web form 有那么多事件的原因.
如果我们只是把后继的中间件中的处理看成一个函数,那么,每个中间件只需要分成 3 步即可:
在 asp.net core 中是使用函数式来实现请求的处理管道的.
在函数式编程中,函数本身是可以作为一个参数来进行传递的。这样可以实现高阶函数。也就是说函数的组合结果还是一个函数.
对于整个处理管道,我们最终希望得到的形式还是一个 requestdelegate,也就是一个对当前请求的 httpcontext 进行处理的函数.
本质上来讲,中间件就是一个用来生成 requestdelegate 对象的生成函数.
为了将多个管道中间件串联起来,每个中间件需要接收下一个中间件的处理请求的函数作为参数,中间件本身返回一个处理请求的 requestdelegate 委托对象。所以,中间件实际上是一个生成器函数.
使用 c# 的委托表示出来,就是下面的一个类型。所以,在 asp.net core 中,中间件的类型就是这个 func<t, tresult>.
1
|
func<requestdelegate, requestdelegate>
|
在 doc 中查看 func<t, tresult> 的文档 。
这个概念比较抽象,与我们所熟悉的面向对象编程方式完全不同,下面我们使用一个示例进行说明.
我们通过一个中间件来演示它的模拟实现代码。下面的代码定义了一个中间件,该中间件接收一个表示后继处理的函数,中间件的返回结果是创建的另外一个 requestdelegate 对象。它的内部通过调用下一个处理函数来完成中间件之间的级联.
1
2
3
4
5
6
7
8
9
10
11
|
// 定义中间件
func<requestdelegate, requestdelegate> middleware1 = next => {
// 中间件返回一个 requestdelegate 对象
return
(httpcontextsample context) => {
// 中间件 1 的处理内容
context.output.appendline(
"middleware 1 processing."
);
// 调用后继的处理函数
return
next(context);
};
};
|
把它和我们前面定义的 app 委托结合起来如下所示,注意调用中间件的结果是返回一个新的委托函数对象,它就是我们的处理管道.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
// 最终的处理函数
requestdelegate app = context =>
{
context.output.appendline(
"end of output."
);
return
task.completedtask;
};
// 定义中间件 1
func<requestdelegate, requestdelegate> middleware1 = next =>
{
return
(httpcontextsample context) =>
{
// 中间件 1 的处理内容
context.output.appendline(
"middleware 1 processing."
);
// 调用后继的处理函数
return
next(context);
};
};
// 得到一个有一个处理步骤的管道
var pipeline1 = middleware1(app);
// 准备一个表示当前请求的对象
var context2 =
new
httpcontextsample();
// 通过管道处理当前请求
pipeline1(context2);
// 输出请求的处理结果
console.writeline(context2.output.tostring());
|
可以得到如下的输出 。
middleware 1 processing. end of output. 。
继续增加第二个中间件来演示多个中间件的级联处理.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
requestdelegate app = context =>
{
context.output.appendline(
"end of output."
);
return
task.completedtask;
};
// 定义中间件 1
func<requestdelegate, requestdelegate> middleware1 = next =>
{
return
(httpcontextsample context) =>
{
// 中间件 1 的处理内容
context.output.appendline(
"middleware 1 processing."
);
// 调用后继的处理函数
return
next(context);
};
};
// 定义中间件 2
func<requestdelegate, requestdelegate> middleware2 = next =>
{
return
(httpcontextsample context) =>
{
// 中间件 2 的处理
context.output.appendline(
"middleware 2 processing."
);
// 调用后继的处理函数
return
next(context);
};
};
// 构建处理管道
var step1 = middleware1(app);
var pipeline2 = middleware2(step1);
// 准备当前的请求对象
var context3 =
new
httpcontextsample();
// 处理请求
pipeline2(context3);
// 输出处理结果
console.writeline(context3.output.tostring());
|
当前的输出 。
middleware 2 processing. middleware 1 processing. end of output. 。
如果我们把这些中间件保存到几个列表中,就可以通过循环来构建处理管道。下面的示例重复使用了前面定义的 app 变量.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
list<func<requestdelegate, requestdelegate>> _components
=
new
list<func<requestdelegate, requestdelegate>>();
_components.add(middleware1);
_components.add(middleware2);
// 构建处理管道
foreach
(var component
in
_components)
{
app = component(app);
}
// 构建请求上下文对象
var context4 =
new
httpcontextsample();
// 使用处理管道处理请求
app(context4);
// 输出处理结果
console.writeline(context4.output.tostring());
|
输出结果与上一示例完全相同 。
middleware 2 processing. middleware 1 processing. end of output. 。
但是,有一个问题,我们后加入到列表中的中间件 2 是先执行的,而先加入到列表中的中间件 1 是后执行的。如果希望实际的执行顺序与加入的顺序一致,只需要将这个列表再反转一下即可.
1
2
3
4
5
6
7
8
9
10
|
// 反转此列表
_components.reverse();
foreach
(var component
in
_components)
{
app = component(app);
}
var context5 =
new
httpcontextsample();
app(context5);
console.writeline(context5.output.tostring());
|
输出结果如下 。
middleware 1 processing. middleware 2 processing. end of output. 。
现在,我们可以回到实际的 asp.net core 代码中,把 asp.net core 中 applicationbuilder 的核心代码 build() 方法抽象之后,可以得到如下的关键代码.
注意 build() 方法就是构建我们的请求处理管道,它返回了一个 requestdelegate 对象,该对象实际上是一个委托对象,代表了一个处理当前请求的处理管道函数,它就是我们所谓的处理管道,以后我们将通过该委托来处理请求.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
requestdelegate build()
{
requestdelegate app = context =>
{
// ......
context.response.statuscode = statuscodes.status404notfound;
return
task.completedtask;
};
foreach
(var component
in
_components.reverse())
{
app = component(app);
}
return
app;
}
|
完整的 applicationbuilder 代码如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
// copyright (c) .net foundation. all rights reserved.
// licensed under the apache license, version 2.0. see license.txt in the project root for license information.
using
system;
using
system.collections.generic;
using
system.linq;
using
system.threading.tasks;
using
microsoft.aspnetcore.http;
using
microsoft.aspnetcore.http.features;
using
microsoft.extensions.
internal
;
namespace
microsoft.aspnetcore.builder
{
public
class
applicationbuilder : iapplicationbuilder
{
private
const
string
serverfeatureskey =
"server.features"
;
private
const
string
applicationserviceskey =
"application.services"
;
private
readonly
ilist<func<requestdelegate, requestdelegate>> _components =
new
list<func<requestdelegate, requestdelegate>>();
public
applicationbuilder(iserviceprovider serviceprovider)
{
properties =
new
dictionary<
string
,
object
?>(stringcomparer.ordinal);
applicationservices = serviceprovider;
}
public
applicationbuilder(iserviceprovider serviceprovider,
object
server)
:
this
(serviceprovider)
{
setproperty(serverfeatureskey, server);
}
private
applicationbuilder(applicationbuilder builder)
{
properties =
new
copyonwritedictionary<
string
,
object
?>(builder.properties, stringcomparer.ordinal);
}
public
iserviceprovider applicationservices
{
get
{
return
getproperty<iserviceprovider>(applicationserviceskey)!;
}
set
{
setproperty<iserviceprovider>(applicationserviceskey, value);
}
}
public
ifeaturecollection serverfeatures
{
get
{
return
getproperty<ifeaturecollection>(serverfeatureskey)!;
}
}
public
idictionary<
string
,
object
?> properties {
get
; }
private
t? getproperty<t>(
string
key)
{
return
properties.trygetvalue(key,
out
var value) ? (t)value :
default
(t);
}
private
void
setproperty<t>(
string
key, t value)
{
properties[key] = value;
}
public
iapplicationbuilder use(func<requestdelegate, requestdelegate> middleware)
{
_components.add(middleware);
return
this
;
}
public
iapplicationbuilder
new
()
{
return
new
applicationbuilder(
this
);
}
public
requestdelegate build()
{
requestdelegate app = context =>
{
// if we reach the end of the pipeline, but we have an endpoint, then something unexpected has happened.
// this could happen if user code sets an endpoint, but they forgot to add the useendpoint middleware.
var endpoint = context.getendpoint();
var endpointrequestdelegate = endpoint?.requestdelegate;
if
(endpointrequestdelegate !=
null
)
{
var message =
$
"the request reached the end of the pipeline without executing the endpoint: '{endpoint!.displayname}'. "
+
$
"please register the endpointmiddleware using '{nameof(iapplicationbuilder)}.useendpoints(...)' if using "
+
$
"routing."
;
throw
new
invalidoperationexception(message);
}
context.response.statuscode = statuscodes.status404notfound;
return
task.completedtask;
};
foreach
(var component
in
_components.reverse())
{
app = component(app);
}
return
app;
}
}
}
|
见:在 github 中查看 applicationbuilder 源码 。
强类型的中间件 。
函数形式的中间件使用比较方便,可以直接在管道定义中使用。但是,如果我们希望能够定义独立的中间件,使用强类型的类来定义会更加方便一些.
1
2
3
4
5
|
public
interface
imiddleware {
public
system.threading.tasks.task invokeasync (
microsoft.aspnetcore.http.httpcontext context,
microsoft.aspnetcore.http.requestdelegate next);
}
|
在 doc 中查看 imiddleware 定义 。
我们定义的强类型中间件可以选择实现装个接口.
next 表示请求处理管道中的下一个中间件,处理管道会将它提供给你定义的中间件。这是将各个中间件连接起来的关键.
如果当前中间件需要将请求继续分发给后继的中间件继续处理,只需要调用这个委托对象即可。否则,应用程序针对该请求的处理到此为止.
例如,增加一个可以添加自定义响应头的中间件,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
using
system.threading.tasks;
public
class
customresponseheader: imiddleware
{
// 使用构造函数完成服务依赖的定义
public
customresponseheader()
{
}
public
task invodeasync(httpcontextsample context, requestdelegate next)
{
context.output.appendline(
"from custom middleware."
);
return
next(context);
}
}
|
这更好看懂了,可是它怎么变成那个 func<requestdelegate, requestdelegate> 呢?
在演示程序中使用该中间件.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
list<func<requestdelegate, requestdelegate>> _components
=
new
list<func<requestdelegate, requestdelegate>>();
_components.add(middleware1);
_components.add(middleware2);
var middleware3 =
new
customresponseheader();
func<requestdelegate, requestdelegate> middleware3 = next =>
{
return
(httpcontextsample context) =>
{
// 中间件 3 的处理
var result = middleware3.invodeasync(context, next);
return
result;
};
};
_components.add(middleware3);
|
这样开发者可以使用熟悉的对象方式开发中间件,而系统内部自动根据你的定义,生成出来一个 func<requestdelegate, requestdelegate> 形式的中间件.
asp.net core 使用该类型中间件的形式如下所示,这是提供了一个方便的扩展方法来完成这个工作.
1
|
.usemiddleware<customresponseheader>();
|
按照约定定义中间件 。
除了实现 imiddleware 这个接口,还可以使用约定方式来创建中间件.
按照约定定义中间件不需要实现某个预定义的接口或者继承某个基类,而是需要遵循一些约定即可。约定主要体现在如下几个方面:
构造函数和 invoke/invokeasync 的其他参数由依赖关系注入 (di) 填充.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
using
system.threading.tasks;
public
class
requestculturemiddleware {
private
readonly
requestdelegate _next;
public
requestculturemiddleware (requestdelegate next) {
_next = next;
}
public
async task invokeasync (httpcontextsample context) {
context.output.appendline(
"middleware 4 processing."
);
// call the next delegate/middleware in the pipeline
await _next (context);
}
}
|
在演示程序中使用按照约定定义的中间件.
1
2
3
4
5
6
7
8
9
10
|
func<requestdelegate, requestdelegate> middleware4 = next => {
return
(httpcontextsample context) => {
var step4 =
new
requestculturemiddleware(next);
// 中间件 4 的处理
var result = step4.invokeasync (context);
return
result;
};
};
_components.add (middleware4);
|
在 asp.net core 中使用按照约定定义的中间件语法与使用强类型方式相同:
1
|
.usemiddleware<requestculturemiddleware >();
|
中间件的顺序 。
中间件安装一定顺寻构造成为请求处理管道,常见的处理管道如下所示:
实现 beginrequest 和 endrequest 。
理解了请求处理管道的原理,下面看它的一个应用.
在 asp.net 中我们可以使用预定义的 begin_request 和 endrequest 处理步骤.
现在整个请求处理管道都是我们自己来进行构建了,那么怎么实现 begin_request 和 endrequest 呢?使用中间件可以很容易实现它.
首先,这两个步骤是请求处理的第一个和最后一个步骤,显然,该中间件必须是第一个注册到管道中的.
所谓的 begin_request 就是在调用 next() 之间的处理了,而 end_request 就是在调用 next() 之后的处理了。在 https://stackoverflow.com/questions/40604609/net-core-endrequest-middleware中就有一个示例,我们将它修改一下,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public
class
beginendrequestmiddleware
{
private
readonly
requestdelegate _next;
public
beginendrequestmiddleware(requestdelegate next)
{
_next = next;
}
public
void
begin_request(httpcontext context) {
// do begin request
}
public
void
end_request(httpcontext context) {
// do end request
}
public
async task invoke(httpcontext context)
{
// do tasks before other middleware here, aka 'beginrequest'
begin_request(context);
// let the middleware pipeline run
await _next(context);
// do tasks after middleware here, aka 'endrequest'
end_request();
}
}
|
register 。
1
2
3
4
5
6
7
8
|
public
void
configure(iapplicationbuilder app)
{
// 第一个注册
app.usemiddleware<beginendrequestmiddleware>();
// register other middelware here such as:
app.usemvc();
}
|
总结 。
到此这篇关于asp.net core处理管道深入理解的文章就介绍到这了,更多相关asp.net core处理管道内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://www.cnblogs.com/haogj/p/13756143.html 。
最后此篇关于ASP.NET Core处理管道的深入理解的文章就讲到这里了,如果你想了解更多关于ASP.NET Core处理管道的深入理解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
对于 Metal ,如果对主纹理进行 mipmap 处理,是否还需要对多采样纹理进行 mipmap 处理?我阅读了苹果文档,但没有得到任何相关信息。 最佳答案 Mipmapping 适用于您将从中
我正在使用的代码在后端 Groovy 代码中具有呈现 GSP(Groovy 服务器页面)的 Controller 。对于前端,我们使用 React-router v4 来处理路由。我遇到的问题是,通过
我们正在 build 一个巨大的网站。我们正在考虑是在服务器端(ASP .Net)还是在客户端进行 HTML 处理。 例如,我们有 HTML 文件,其作用类似于用于生成选项卡的模板。服务器端获取 HT
我正在尝试将图像加载到 void setup() 中的数组中,但是当我这样做时出现此错误:“类型不匹配,'processing .core.PImage' does not匹配“processing.
我正在尝试使用其私有(private)应用程序更新 Shopify 上的客户标签。我用 postman 尝试过,一切正常,但通过 AJAX,它带我成功回调而不是错误,但成功后我得到了身份验证链接,而不
如何更改我的 Processing appIconTest.exe 导出的默认图标在窗口中的应用程序? 默认一个: 最佳答案 经过一些研究,我能找到的最简单的解决方案是: 进入 ...\process
我在 Processing 中做了一个简单的小游戏,但需要一些帮助。我有一个 mp3,想将它添加到我的应用程序中,以便在后台循环运行。 这可能吗?非常感谢。 最佳答案 您可以使用声音库。处理已经自带
我有几个这样创建的按钮: 在 setup() PImage[] imgs1 = {loadImage("AREA1_1.png"),loadImage("AREA1_2.png"),loadImage
我正在尝试使用 Processing 创建一个多人游戏,但无法弄清楚如何将屏幕分成两个以显示玩家的不同情况? 就像在 c# 中一样,我们有Viewport leftViewport,rightView
我一直在尝试使用 Moore 邻域在处理过程中创建元胞自动机,到目前为止非常成功。我已经设法使基本系统正常工作,现在我希望通过添加不同的功能来使用它。现在,我检查细胞是否存活。如果是,我使用 fill
有没有办法用 JavaScript 代码检查资源使用情况?我可以检查脚本的 RAM 使用情况和 CPU 使用情况吗? 由于做某事有多种方法,我可能会使用不同的方法编写代码,并将其保存为两个不同的文件,
我想弄清楚如何处理这样的列表: [ [[4,6,7], [1,2,4,6]] , [[10,4,2,4], [1]] ] 这是一个整数列表的列表 我希望我的函数将此列表作为输入并返回列表中没有重复的整
有没有办法在不需要时处理 MethodChannel/EventChannel ?我问是因为我想为对象创建多个方法/事件 channel 。 例子: class Call { ... fields
我有一个关于在 Python3 中处理 ConnectionResetError 的问题。这通常发生在我使用 urllib.request.Request 函数时。我想知道如果我们遇到这样的错误是否可
我一直在努力解决这个问题几个小时,但无济于事。代码很简单,一个弹跳球(粒子)。将粒子的速度初始化为 (0, 0) 将使其保持上下弹跳。将粒子的初始化速度更改为 (0, 0.01) 或任何十进制浮点数都
我把自己弄得一团糟。 我想在我的系统中添加 python3.6 所以我决定在我的 Ubuntu 19.10 中卸载现有的。但是现在每次我想安装一些东西我都会得到这样的错误: dpkg: error w
我正在努力解决 Rpart 包中的 NA 功能。我得到了以下数据框(下面的代码) Outcome VarA VarB 1 1 1 0 2 1 1 1
我将 Java 与 JSF 一起使用,这是 Glassfish 3 容器。 在我的 Web 应用程序中,我试图实现一个文件(图像)管理系统。 我有一个 config.properties我从中读取上传
所以我一直在Processing工作几个星期以来,虽然我没有编程经验,但我已经转向更复杂的项目。我正在编写一个进化模拟器,它会产生具有随机属性的生物。 最终,我将添加复制,但现在这些生物只是在屏幕上漂
有人知道 Delphi 2009 对“with”的处理有什么不同吗? 我昨天解决了一个问题,只是将“with”解构为完整引用,如“with Datamodule、Dataset、MainForm”。
我是一名优秀的程序员,十分优秀!