. NET Core Development Log--requestdelegate

Source: Internet
Author: User
Tags httpcontext

This article is primarily an addendum to the. NET Core Development Log--middleware, but it will start with a seemingly flat requestdelegate, so it is reasonable to use it as a headline.

Requestdelegate is a delegate type whose full picture is public delegate Task RequestDelegate(HttpContext context) , on MSDN, an explanation of it, "A function that can process an HTTP request." --functions that handle HTTP requests. The only parameter is the most familiar HttpContext, and the return value is the type of asynchronous operation that represents the completion of the request processing.

It can be understood as an abstraction of all HTTP request processing in ASP. (The delegate type itself can be considered a function template, its implementation has a uniform parameter list and return value type), without which the entire framework loses its ability to handle HTTP requests.

And it is also the cornerstone of the formation of middleware. Or more accurately, the type of the argument and the return value are the Func<RequestDelegate, RequestDelegate> core gears that keep the middleware running.

The place where the gears are assembled is located within the Applicationbuilder class, which contains a collection of all the gears.

private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();

And how to add gears:

public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware){    _components.Add(middleware);    return this;}

In the Configure method of the Startup class, you can complete the simplest middleware by invoking the use method of the applicationbuilder above.

public void Configure(IApplicationBuilder app){    app.Use(_ =>    {        return context =>        {            return context.Response.WriteAsync("Hello, World!");        };            });}

To become middleware, the gears need to be assembled after the addition is completed.

public RequestDelegate Build(){    RequestDelegate app = context =>    {        context.Response.StatusCode = 404;        return Task.CompletedTask;    };    foreach (var component in _components.Reverse())    {        app = component(app);    }    return app;}

The build method defines the bottom-most part--app, context => { context.Response.StatusCode = 404; return Task.CompletedTask; } which means that if you do not add any middleware, the ASP. NET core site will start up with a 404 error.

The next section, traversing the gears in reverse order, began to be formally assembled.

In the above example, only one gear is used:

_ =>{    return context =>    {        return context.Response.WriteAsync("Hello, World!");    };    }

So after the first and last loop, the component(app) app is re-assigned to the following:

context => context.Response.WriteAsync("Hello, World!");

The result of the assembly is the value of the app.

This assembly process begins when the webhost is buildapplication. As can be seen from the return value type of this method, although the Ming Yi is created application, the requestdelegate is actually generated.

private RequestDelegate BuildApplication(){    try    {        ...        var builderFactory = _applicationServices.GetRequiredService<IApplicationBuilderFactory>();        var builder = builderFactory.CreateBuilder(Server.Features);        ...        Action<IApplicationBuilder> configure = _startup.Configure;        ...        configure(builder);        return builder.Build();    }    ...}    

And this requestdelegate will eventually be called in the Processrequestasync method of the Hostingapplication class.

public virtual async Task StartAsync(CancellationToken cancellationToken = default){    ...    var application = BuildApplication();    ...    var hostingApp = new HostingApplication(application, _logger, diagnosticSource, httpContextFactory);    ...}    public HostingApplication(    RequestDelegate application,    ILogger logger,    DiagnosticListener diagnosticSource,    IHttpContextFactory httpContextFactory){    _application = application;    _diagnostics = new HostingApplicationDiagnostics(logger, diagnosticSource);    _httpContextFactory = httpContextFactory;}public Task ProcessRequestAsync(Context context){    return _application(context.HttpContext);}

The result of the above example is to display the Hello, world! character.

A 404 error no longer occurs, meaning that the middleware only completes its own processing of the HTTP request and does not pass the request to the next layer of middleware.

Another use extension method is required to achieve the purpose of continually transmitting requests.

public static IApplicationBuilder Use(this IApplicationBuilder app, Func<HttpContext, Func<Task>, Task> middleware){    return app.Use(next =>    {        return context =>        {            Func<Task> simpleNext = () => next(context);            return middleware(context, simpleNext);        };    });}

This can be written in the actual code:

public void Configure(IApplicationBuilder app){    app.Use(async (context, next) =>    {        await context.Response.WriteAsync("I am a Middleware!\n");        await next.Invoke();    });    app.Use(_ =>    {        return context =>        {            return context.Response.WriteAsync("Hello, World!");        };    });}

Now there is a middleware, continue the assembly process above. The value of the app is eventually assigned to:

async context =>{    Func<Task> simpleNext = () => context.Response.WriteAsync("Hello, World!");     await context.Response.WriteAsync("I am a Middleware!\n");    await simpleNext.Invoke();};

The results shown are:

I am a Middleware!Hello, World!

This process can be clearly explained in the flowchart below.

If you await next.Invoke() drop the comment,

public void Configure(IApplicationBuilder app){    app.Use(async (context, next) =>    {        await context.Response.WriteAsync("I am a Middleware!\n");        //await next.Invoke();    });    app.Use(_ =>    {        return context =>        {            return context.Response.WriteAsync("Hello, World!");        };    });}

After the first middleware is processed in the previous example, the second middleware processing will not continue. Note that the following Simplenext methods are only defined and not called.

async context =>{    Func<Task> simpleNext = () => context.Response.WriteAsync("Hello, World!");     await context.Response.WriteAsync("I am a Middleware!\n");};

This condition is known as short-circuit (short-circuiting).

Short-circuit middleware are generally placed at the end of all middleware, as the end of the entire pipeline.

And the more common way is to use the run extension method.

public static void Run(this IApplicationBuilder app, RequestDelegate handler){    ...    app.Use(_ => handler);}

So you can change the code of the above example into the following form:

public void Configure(IApplicationBuilder app){    app.Use(async (context, next) =>    {        await context.Response.WriteAsync("I am a Middleware!\n");        await next.Invoke();    });    app.Run(async context =>    {        await context.Response.WriteAsync("Hello, World!");    });}

In addition to short-circuiting, middleware can also have branches when processing.

public void Configure(IApplicationBuilder app){    app.Map("/branch1", ab => {        ab.Run(async context =>        {            await context.Response.WriteAsync("Map branch 1");        });    });    app.Map("/branch2", ab => {        ab.Run(async context =>        {            await context.Response.WriteAsync("Map branch 2");        });    });    app.Use(async (context, next) =>    {        await context.Response.WriteAsync("I am a Middleware!\n");        await next.Invoke();    });    app.Run(async context =>    {        await context.Response.WriteAsync("Hello, World!");    });}

After the URL address is followed by branch1:

After the URL address is followed by BRANCH2:

In other cases:

Code implementation of the map extension method:

public static IApplicationBuilder Map(this IApplicationBuilder app, PathString pathMatch, Action<IApplicationBuilder> configuration){    ...    // create branch    var branchBuilder = app.New();    configuration(branchBuilder);    var branch = branchBuilder.Build();    var options = new MapOptions    {        Branch = branch,        PathMatch = pathMatch,    };    return app.Use(next => new MapMiddleware(next, options).Invoke);}

The way to create a branch is to re-instantiate a applicationbuilder.

public IApplicationBuilder New(){    return new ApplicationBuilder(this);}

The processing of branches is encapsulated in the Mapmiddleware class.

public async Task Invoke(HttpContext context){    ...    PathString matchedPath;    PathString remainingPath;    if (context.Request.Path.StartsWithSegments(_options.PathMatch, out matchedPath, out remainingPath))    {        // Update the path        var path = context.Request.Path;        var pathBase = context.Request.PathBase;        context.Request.PathBase = pathBase.Add(matchedPath);        context.Request.Path = remainingPath;        try        {            await _options.Branch(context);        }        finally        {            context.Request.PathBase = pathBase;            context.Request.Path = path;        }    }    else    {        await _next(context);    }}

Speaking of Mapmiddleware, we have to mention various extension methods that begin with use, such as usestaticfiles,usemvc,usepathbase and so on.

These methods call the Usemiddleware method internally to use a variety of custom middleware classes. As the following Usepathbase code:

public static IApplicationBuilder UsePathBase(this IApplicationBuilder app, PathString pathBase){    ...    // Strip trailing slashes    pathBase = pathBase.Value?.TrimEnd(‘/‘);    if (!pathBase.HasValue)    {        return app;    }    return app.UseMiddleware<UsePathBaseMiddleware>(pathBase);}

It can be learned from the Usemiddleware method that the middleware class needs to satisfy one of the two conditions to be effectively used. One is to implement Imiddleware, and the other is to have an invoke or InvokeAsync method, and the method must have at least a HttpContext type parameter (it can only be the first one), and the return value needs to be a task type.

Internal const string invokemethodname = "Invoke"; internal const string invokeasyncmethodname = "InvokeAsync";p ublic stat IC Iapplicationbuilder usemiddleware (this iapplicationbuilder app, Type middleware, params object[] args) {if (typeof (I Middleware). GetTypeInfo (). IsAssignableFrom (middleware.    GetTypeInfo ())) {... return usemiddlewareinterface (app, middleware); } var applicationservices = app.    ApplicationServices; Return app. Use (next = {var methods = middleware. GetMethods (BindingFlags.Instance |        BindingFlags.Public); var invokemethods = methods. Where (M = string. Equals (M.name, Invokemethodname, stringcomparison.ordinal) | | String. Equals (M.name, Invokeasyncmethodname, stringcomparison.ordinal)).        ToArray (); ... var ctorargs = new Object[args.        Length + 1];        Ctorargs[0] = next; Array.copy (args, 0, Ctorargs, 1, args.        Length); var instance = Activatorutilities.createinstance (App.        ApplicationServices, middleware, Ctorargs); if (parameters. Length = = 1) {return (requestdelegate) MethodInfo.        CreateDelegate (typeof (Requestdelegate), instance);        } var factory = compile<object> (methodinfo, parameters); return context = {var serviceprovider = context. Requestservices??            ApplicationServices;        ... return factory (instance, context, serviceprovider);    }; });}

The introduction of middleware to ASP. NET core finally can be finished, I hope these two articles can provide readers with a little help.

. NET Core Development Log--requestdelegate

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.