Вы находитесь на странице: 1из 10

ASP.

NET Core Middleware Fundamentals


Invalid Date 8 min. pentru citire Colegi toate

n acest articol

What is middleware

Creating a middleware pipeline with IApplicationBuilder

Ordering

Builtin middleware

Writing middleware

Resources

By Rick Anderson and Steve Smith

View or download sample code

What is middleware
Middleware is software that is assembled into an application pipeline to handle requests and
responses. Each component chooses whether to pass the request on to the next component
in the pipeline, and can perform certain actions before and after the next component is
invoked in the pipeline. Request delegates are used to build the request pipeline. The request
delegates handle each HTTP request.

Request delegates are configured using Run, Map, and Use extension methods on the
IApplicationBuilder instance that is passed into the Configure method in the Startup class.
An individual request delegate can be specified inline as an anonymous method called in
line middleware, or it can be defined in a reusable class. These reusable classes and inline
anonymous methods are middleware, or middleware components. Each middleware
component in the request pipeline is responsible for invoking the next component in the
pipeline, or shortcircuiting the chain if appropriate.

Migrating HTTP Modules to Middleware explains the difference between request pipelines in
ASP.NET Core and the previous versions and provides more middleware samples.

Creating a middleware pipeline with


IApplicationBuilder
The ASP.NET Core request pipeline consists of a sequence of request delegates, called one
after the other, as this diagram shows the thread of execution follows the black arrows:

Each delegate can perform operations before and after the next delegate. A delegate can also
decide to not pass a request to the next delegate, which is called shortcircuiting the request
pipeline. Shortcircuiting is often desirable because it allows unnecessary work to be avoided.
For example, the static file middleware can return a request for a static file and shortcircuit
the rest of the pipeline. Exceptionhandling delegates need to be called early in the pipeline,
so they can catch exceptions that occur in later stages of the pipeline.

The simplest possible ASP.NET Core app sets up a single request delegate that handles all
requests. This case doesn't include an actual request pipeline. Instead, a single anonymous
function is called in response to every HTTP request.

C# Copy

usingMicrosoft.AspNetCore.Builder;
usingMicrosoft.AspNetCore.Hosting;
usingMicrosoft.AspNetCore.Http;

publicclassStartup
{
publicvoidConfigure(IApplicationBuilderapp)
{
app.Run(asynccontext=>
{
awaitcontext.Response.WriteAsync("Hello,World!");
});
}
}
The first app.Run delegate terminates the pipeline.

You can chain multiple request delegates together with app.Use. The next parameter
represents the next delegate in the pipeline. Remember that you can shortcircuit the
pipeline by not calling the next parameter. You can typically perform actions both before and
after the next delegate, as this example demonstrates:

C# Copy

publicclassStartup
{
publicvoidConfigure(IApplicationBuilderapp)
{
app.Use(async(context,next)=>
{
//Doworkthatdoesn'twritetotheResponse.
awaitnext.Invoke();
//Dologgingorotherworkthatdoesn'twritetotheResponse.
});

app.Run(asynccontext=>
{
awaitcontext.Response.WriteAsync("Hellofrom2nddelegate.");
});
}
}

Warning
Be careful modifying the HttpResponse after invoking next , because the response may have
already been sent to the client. You can use HttpResponse.HasStarted to check whether the headers
have been sent.

Warning
Do not call next.Invoke after calling a write method. A middleware component either
produces a response or calls next.Invoke , but not both.

Ordering
The order that middleware components are added in the Configure method defines the
order in which they are invoked on requests, and the reverse order for the response. This
ordering is critical for security, performance, and functionality.

The Configure method shown below adds the following middleware components:

1. Exception/error handling
2. Static file server
3. Authentication
4. MVC

C# Copy

publicvoidConfigure(IApplicationBuilderapp)
{
app.UseExceptionHandler("/Home/Error");//Callfirsttocatchexceptions
//throwninthefollowingmiddleware.

app.UseStaticFiles();//Returnstaticfilesandendpipeline.

app.UseIdentity();//Authenticatebeforeyouaccess
//secureresources.

app.UseMvcWithDefaultRoute();//AddMVCtotherequestpipeline.
}

In the code above, UseExceptionHandler is the first middleware component added to the
pipelinetherefore, it catches any exceptions that occur in later calls.

The static file middleware is called early in the pipeline so it can handle requests and short
circuit without going through the remaining components. The static file middleware provides
no authorization checks. Any files served by it, including those under wwwroot, are publicly
available. See Working with static files for an approach to secure static files.

If the request is not handled by the static file middleware, it's passed on to the Identity
middleware app.UseIdentity , which performs authentication. Identity does not shortcircuit
unauthenticated requests. Although Identity authenticates requests, authorization and
rejection occurs only after MVC selects a specific controller and action.

The following example demonstrates a middleware ordering where requests for static files
are handled by the static file middleware before the response compression middleware. Static
files are not compressed with this ordering of the middleware. The MVC responses from
UseMvcWithDefaultRoute can be compressed.

C# Copy

publicvoidConfigure(IApplicationBuilderapp)
{
app.UseStaticFiles();//Staticfilesnotcompressed
//bymiddleware.
app.UseResponseCompression();
app.UseMvcWithDefaultRoute();
}

Run, Map, and Use


You configure the HTTP pipeline using Run , Map , and Use . The Run method short
circuits the pipeline that is, it does not call a next request delegate. Run is a convention,
and some middleware components may expose Run[Middleware] methods that run at the
end of the pipeline.

Map* extensions are used as a convention for branching the pipeline. Map branches the
request pipeline based on matches of the given request path. If the request path starts with
the given path, the branch is executed.

C# Copy

publicclassStartup
{
privatestaticvoidHandleMapTest1(IApplicationBuilderapp)
{
app.Run(asynccontext=>
{
awaitcontext.Response.WriteAsync("MapTest1");
});
}

privatestaticvoidHandleMapTest2(IApplicationBuilderapp)
{
app.Run(asynccontext=>
{
awaitcontext.Response.WriteAsync("MapTest2");
});
}

publicvoidConfigure(IApplicationBuilderapp)
{
app.Map("/map1",HandleMapTest1);

app.Map("/map2",HandleMapTest2);

app.Run(asynccontext=>
{
awaitcontext.Response.WriteAsync("HellofromnonMapdelegate.<p>");
});
}
}

The following table shows the requests and responses from http://localhost:1234 using the
previous code:
Request Response

localhost:1234 Hello from nonMap delegate.

localhost:1234/map1 Map Test 1

localhost:1234/map2 Map Test 2


Request Response

localhost:1234/map3 Hello from nonMap delegate.

When Map is used, the matched path segments are removed from HttpRequest.Path and
appended to HttpRequest.PathBase for each request.

MapWhen branches the request pipeline based on the result of the given predicate. Any
predicate of type Func<HttpContext,bool> can be used to map requests to a new branch of
the pipeline. In the following example, a predicate is used to detect the presence of a query
string variable branch :

C# Copy

publicclassStartup
{
privatestaticvoidHandleBranch(IApplicationBuilderapp)
{
app.Run(asynccontext=>
{
varbranchVer=context.Request.Query["branch"];
awaitcontext.Response.WriteAsync($"Branchused={branchVer}");
});
}

publicvoidConfigure(IApplicationBuilderapp)
{
app.MapWhen(context=>context.Request.Query.ContainsKey("branch"),
HandleBranch);

app.Run(asynccontext=>
{
awaitcontext.Response.WriteAsync("HellofromnonMapdelegate.<p>");
});
}
}

The following table shows the requests and responses from http://localhost:1234 using the
previous code:
Request Response

localhost:1234 Hello from nonMap delegate.

localhost:1234/?branch=master Branch used = master

Map supports nesting, for example:

C# Copy
app.Map("/level1",level1App=>{
level1App.Map("/level2a",level2AApp=>{
//"/level1/level2a"
//...
});
level1App.Map("/level2b",level2BApp=>{
//"/level1/level2b"
//...
});
});

Map can also match multiple segments at once, for example:

C# Copy

app.Map("/level1/level2",HandleMultiSeg);

Builtin middleware
ASP.NET Core ships with the following middleware components:
Middleware Description

Authentication Provides authentication support.

CORS Configures CrossOrigin Resource Sharing.

Response Caching Provides support for caching responses.

Response Compression Provides support for compressing responses.

Routing Defines and constrains request routes.

Session Provides support for managing user sessions.

Static Files Provides support for serving static files and directory browsing.

URL Rewriting Middleware Provides support for rewriting URLs and redirecting requests.

Writing middleware
Middleware is generally encapsulated in a class and exposed with an extension method.
Consider the following middleware, which sets the culture for the current request from the
query string:

C# Copy
publicclassStartup
{
publicvoidConfigure(IApplicationBuilderapp)
{
app.Use((context,next)=>
{
varcultureQuery=context.Request.Query["culture"];
if(!string.IsNullOrWhiteSpace(cultureQuery))
{
varculture=newCultureInfo(cultureQuery);

CultureInfo.CurrentCulture=culture;
CultureInfo.CurrentUICulture=culture;
}

//Callthenextdelegate/middlewareinthepipeline
returnnext();
});

app.Run(async(context)=>
{
awaitcontext.Response.WriteAsync(
$"Hello{CultureInfo.CurrentCulture.DisplayName}");
});

}
}

Note: The sample code above is used to demonstrate creating a middleware component. See
Globalization and localization for ASP.NET Core's builtin localization support.

You can test the middleware by passing in the culture, for example
http://localhost:7997/?culture=no .

The following code moves the middleware delegate to a class:

C# Copy

usingMicrosoft.AspNetCore.Http;
usingSystem.Globalization;
usingSystem.Threading.Tasks;

namespaceCulture
{
publicclassRequestCultureMiddleware
{
privatereadonlyRequestDelegate_next;

publicRequestCultureMiddleware(RequestDelegatenext)
{
_next=next;
}

publicTaskInvoke(HttpContextcontext)
{
varcultureQuery=context.Request.Query["culture"];
if(!string.IsNullOrWhiteSpace(cultureQuery))
{
varculture=newCultureInfo(cultureQuery);

CultureInfo.CurrentCulture=culture;
CultureInfo.CurrentUICulture=culture;

//Callthenextdelegate/middlewareinthepipeline
returnthis._next(context);
}
}
}

The following extension method exposes the middleware through IApplicationBuilder:

C# Copy

usingMicrosoft.AspNetCore.Builder;

namespaceCulture
{
publicstaticclassRequestCultureMiddlewareExtensions
{
publicstaticIApplicationBuilderUseRequestCulture(
thisIApplicationBuilderbuilder)
{
returnbuilder.UseMiddleware<RequestCultureMiddleware>();
}
}
}

The following code calls the middleware from Configure :

C# Copy

publicclassStartup
{
publicvoidConfigure(IApplicationBuilderapp)
{
app.UseRequestCulture();

app.Run(async(context)=>
{
awaitcontext.Response.WriteAsync(
$"Hello{CultureInfo.CurrentCulture.DisplayName}");
});

}
}

Middleware should follow the Explicit Dependencies Principle by exposing its dependencies
in its constructor. Middleware is constructed once per application lifetime. See Perrequest
dependencies below if you need to share services with middleware within a request.

Middleware components can resolve their dependencies from dependency injection through
constructor parameters. UseMiddleware<T> can also accept additional parameters directly.

Perrequest dependencies

Because middleware is constructed at app startup, not perrequest, scoped lifetime services
used by middleware constructors are not shared with other dependencyinjected types
during each request. If you must share a scoped service between your middleware and other
types, add these services to the Invoke method's signature. The Invoke method can accept
additional parameters that are populated by dependency injection. For example:

c# Copy

publicclassMyMiddleware
{
privatereadonlyRequestDelegate_next;

publicMyMiddleware(RequestDelegatenext)
{
_next=next;
}

publicasyncTaskInvoke(HttpContexthttpContext,IMyScopedServicesvc)
{
svc.MyProperty=1000;
await_next(httpContext);
}
}

Resources
Sample code used in this doc
Migrating HTTP Modules to Middleware
Application Startup
Request Features

Вам также может понравиться