前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >netcore无线路由器_netcore路由器怎么设置

netcore无线路由器_netcore路由器怎么设置

作者头像
全栈程序员站长
发布2022-09-23 19:23:29
3K0
发布2022-09-23 19:23:29
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

IdentityServer里有各种Endpoint,如TokenEndpoint,UserInfoEndpoint,Authorize Endpoint,Discovery Endpoint等等。Endpoint从字面意思来看是“终端节点”或者“终节点”的意思。无独有偶NetCore的路由也有Endpoint的概念。那么我们提出一个问题来,究竟什么是Endpoint?

先来看一段代码,如下所示:

代码语言:javascript
复制
public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureServices(svcs => svcs.AddRouting()).Configure( app => app.UseRouting().UseEndpoints( //test1 //endpoints => endpoints.MapGet("weather/{city}/{days}", //test2 endpoints =>endpoints.MapGet("weather/{city}/{year}.{month}.{day}", WeatherForecast) )); }); private static Dictionary<string, string> _cities = new Dictionary<string, string>() { ["001"]="Beijing", ["028"]="Chengdu", ["091"]="Suzhou" }; //RequestDelegate public static async Task WeatherForecast(HttpContext context) { var city = (string)context.GetRouteData().Values["city"]; city = _cities[city]; //test1 //int days=int.Parse(context.GetRouteData().Values["days"].ToString()); //var report = new WeatherReport(city, days); //test2 int year = int.Parse(context.GetRouteData().Values["year"].ToString()); int month= int.Parse(context.GetRouteData().Values["month"].ToString()); int day=int.Parse(context.GetRouteData().Values["day"].ToString()); var report = new WeatherReport(city, new DateTime(year, month, day)); await RenderWeatherAsync(context, report); } private static async Task RenderWeatherAsync(HttpContext context,WeatherReport report) { context.Response.ContentType = "text/html;charset=utf-8"; await context.Response.WriteAsync("<html><head><title>Weather</title></head><body>"); await context.Response.WriteAsync($"<h3>{report.City}</h3>"); foreach(var it in report.WeatherInfos) { await context.Response.WriteAsync($"{it.Key.ToString("yyyy-MM-dd")}: "); await context.Response.WriteAsync($"{it.Value.LowTemperature}--{it.Value.HighTemperature} <br/>"); } await context.Response.WriteAsync("</body></html>"); } }

View Code

上面的代码很清晰,如果路由格式匹配”weather/{city}/{year}.{month}.{day}”,那么就调用WeatherForecast方法,这里的app.UseRouting().UseEndpoints就用到了Endpoint模式。要了解Endpoint,我们需要抽丝剥茧一步一步了解几个类的方法:

1.我们先看最里面的endpoints =>endpoints.MapGet(“weather/{city}/{year}.{month}.{day}”, WeatherForecast),这里实际上调用的是EndpointRouteBuilderExtensions类的MapGet方法,代码如下所示:

代码语言:javascript
复制
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释 
* /// <summary> /// Provides extension methods for <see cref="IEndpointRouteBuilder"/> to add endpoints. /// </summary> public static class EndpointRouteBuilderExtensions { // Avoid creating a new array every call private static readonly string[] GetVerb = new[] { "GET" }; private static readonly string[] PostVerb = new[] { "POST" }; private static readonly string[] PutVerb = new[] { "PUT" }; private static readonly string[] DeleteVerb = new[] { "DELETE" }; private static readonly string[] PatchVerb = new[] { "PATCH" }; /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP GET requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns> public static IEndpointConventionBuilder MapGet( this IEndpointRouteBuilder endpoints, string pattern, RequestDelegate requestDelegate) { return MapMethods(endpoints, pattern, GetVerb, requestDelegate); } /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP POST requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns> public static IEndpointConventionBuilder MapPost( this IEndpointRouteBuilder endpoints, string pattern, RequestDelegate requestDelegate) { return MapMethods(endpoints, pattern, PostVerb, requestDelegate); } /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP PUT requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns> public static IEndpointConventionBuilder MapPut( this IEndpointRouteBuilder endpoints, string pattern, RequestDelegate requestDelegate) { return MapMethods(endpoints, pattern, PutVerb, requestDelegate); } /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP DELETE requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns> public static IEndpointConventionBuilder MapDelete( this IEndpointRouteBuilder endpoints, string pattern, RequestDelegate requestDelegate) { return MapMethods(endpoints, pattern, DeleteVerb, requestDelegate); } /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP PATCH requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns> public static IEndpointConventionBuilder MapPatch( this IEndpointRouteBuilder endpoints, string pattern, RequestDelegate requestDelegate) { return MapMethods(endpoints, pattern, PatchVerb, requestDelegate); } /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP requests /// for the specified HTTP methods and pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param> /// <param name="httpMethods">HTTP methods that the endpoint will match.</param> /// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns> public static IEndpointConventionBuilder MapMethods( this IEndpointRouteBuilder endpoints, string pattern, IEnumerable<string> httpMethods, RequestDelegate requestDelegate) { if (httpMethods == null) { throw new ArgumentNullException(nameof(httpMethods)); } var builder = endpoints.Map(RoutePatternFactory.Parse(pattern), requestDelegate); builder.WithDisplayName($"{pattern} HTTP: {string.Join(", ", httpMethods)}"); builder.WithMetadata(new HttpMethodMetadata(httpMethods)); return builder; } /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns> public static IEndpointConventionBuilder Map( this IEndpointRouteBuilder endpoints, string pattern, RequestDelegate requestDelegate) { return Map(endpoints, RoutePatternFactory.Parse(pattern), requestDelegate); } /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns> public static IEndpointConventionBuilder Map( this IEndpointRouteBuilder endpoints, RoutePattern pattern, RequestDelegate requestDelegate) { if (endpoints == null) { throw new ArgumentNullException(nameof(endpoints)); } if (pattern == null) { throw new ArgumentNullException(nameof(pattern)); } if (requestDelegate == null) { throw new ArgumentNullException(nameof(requestDelegate)); } const int defaultOrder = 0; //实例化RouteEndpointBuilder var builder = new RouteEndpointBuilder( requestDelegate, pattern, defaultOrder) { DisplayName = pattern.RawText ?? pattern.DebuggerToString(), }; // Add delegate attributes as metadata var attributes = requestDelegate.Method.GetCustomAttributes(); // This can be null if the delegate is a dynamic method or compiled from an expression tree if (attributes != null) { foreach (var attribute in attributes) { builder.Metadata.Add(attribute); } } //Adds a RouteEndpoint to the IEndpointRouteBuilder that matches HTTP requests /// for the specified pattern. ///可以理解为:将某种格式的路由pattern添加至IEndpointRouteBuilder的DataSource属性,为match做准备  var dataSource = endpoints.DataSources.OfType<ModelEndpointDataSource>().FirstOrDefault(); if (dataSource == null) { dataSource = new ModelEndpointDataSource(); endpoints.DataSources.Add(dataSource); } return dataSource.AddEndpointBuilder(builder); } /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP GET requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="handler">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the endpoint.</returns> public static RouteHandlerBuilder MapGet( this IEndpointRouteBuilder endpoints, string pattern, Delegate handler) { return MapMethods(endpoints, pattern, GetVerb, handler); } /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP POST requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="handler">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the endpoint.</returns> public static RouteHandlerBuilder MapPost( this IEndpointRouteBuilder endpoints, string pattern, Delegate handler) { return MapMethods(endpoints, pattern, PostVerb, handler); } /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP PUT requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="handler">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the endpoint.</returns> public static RouteHandlerBuilder MapPut( this IEndpointRouteBuilder endpoints, string pattern, Delegate handler) { return MapMethods(endpoints, pattern, PutVerb, handler); } /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP DELETE requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="handler">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the endpoint.</returns> public static RouteHandlerBuilder MapDelete( this IEndpointRouteBuilder endpoints, string pattern, Delegate handler) { return MapMethods(endpoints, pattern, DeleteVerb, handler); } /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP PATCH requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="handler">The <see cref="Delegate" /> executed when the endpoint is matched.</param> /// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the endpoint.</returns> public static RouteHandlerBuilder MapPatch( this IEndpointRouteBuilder endpoints, string pattern, Delegate handler) { return MapMethods(endpoints, pattern, PatchVerb, handler); } /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP requests /// for the specified HTTP methods and pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="handler">The delegate executed when the endpoint is matched.</param> /// <param name="httpMethods">HTTP methods that the endpoint will match.</param> /// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the endpoint.</returns> public static RouteHandlerBuilder MapMethods( this IEndpointRouteBuilder endpoints, string pattern, IEnumerable<string> httpMethods, Delegate handler) { if (httpMethods is null) { throw new ArgumentNullException(nameof(httpMethods)); } var disableInferredBody = false; foreach (var method in httpMethods) { disableInferredBody = ShouldDisableInferredBody(method); if (disableInferredBody is true) { break; } } var builder = endpoints.Map(RoutePatternFactory.Parse(pattern), handler, disableInferredBody); // Prepends the HTTP method to the DisplayName produced with pattern + method name builder.Add(b => b.DisplayName = $"HTTP: {string.Join(", ", httpMethods)} {b.DisplayName}"); builder.WithMetadata(new HttpMethodMetadata(httpMethods)); return builder; static bool ShouldDisableInferredBody(string method) { // GET, DELETE, HEAD, CONNECT, TRACE, and OPTIONS normally do not contain bodies return method.Equals(HttpMethods.Get, StringComparison.Ordinal) || method.Equals(HttpMethods.Delete, StringComparison.Ordinal) || method.Equals(HttpMethods.Head, StringComparison.Ordinal) || method.Equals(HttpMethods.Options, StringComparison.Ordinal) || method.Equals(HttpMethods.Trace, StringComparison.Ordinal) || method.Equals(HttpMethods.Connect, StringComparison.Ordinal); } } /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="handler">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the endpoint.</returns> public static RouteHandlerBuilder Map( this IEndpointRouteBuilder endpoints, string pattern, Delegate handler) { return Map(endpoints, RoutePatternFactory.Parse(pattern), handler); } /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="handler">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the endpoint.</returns> public static RouteHandlerBuilder Map( this IEndpointRouteBuilder endpoints, RoutePattern pattern, Delegate handler) { return Map(endpoints, pattern, handler, disableInferBodyFromParameters: false); } /// <summary> /// Adds a specialized <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that will match /// requests for non-file-names with the lowest possible priority. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="handler">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the endpoint.</returns> /// <remarks> /// <para> /// <see cref="MapFallback(IEndpointRouteBuilder, Delegate)"/> is intended to handle cases where URL path of /// the request does not contain a file name, and no other endpoint has matched. This is convenient for routing /// requests for dynamic content to a SPA framework, while also allowing requests for non-existent files to /// result in an HTTP 404. /// </para> /// <para> /// <see cref="MapFallback(IEndpointRouteBuilder, Delegate)"/> registers an endpoint using the pattern /// <c>{*path:nonfile}</c>. The order of the registered endpoint will be <c>int.MaxValue</c>. /// </para> /// </remarks> public static RouteHandlerBuilder MapFallback(this IEndpointRouteBuilder endpoints, Delegate handler) { if (endpoints == null) { throw new ArgumentNullException(nameof(endpoints)); } if (handler == null) { throw new ArgumentNullException(nameof(handler)); } return endpoints.MapFallback("{*path:nonfile}", handler); } /// <summary> /// Adds a specialized <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that will match /// the provided pattern with the lowest possible priority. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="handler">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the endpoint.</returns> /// <remarks> /// <para> /// <see cref="MapFallback(IEndpointRouteBuilder, string, Delegate)"/> is intended to handle cases where no /// other endpoint has matched. This is convenient for routing requests to a SPA framework. /// </para> /// <para> /// The order of the registered endpoint will be <c>int.MaxValue</c>. /// </para> /// <para> /// This overload will use the provided <paramref name="pattern"/> verbatim. Use the <c>:nonfile</c> route constraint /// to exclude requests for static files. /// </para> /// </remarks> public static RouteHandlerBuilder MapFallback( this IEndpointRouteBuilder endpoints, string pattern, Delegate handler) { if (endpoints == null) { throw new ArgumentNullException(nameof(endpoints)); } if (pattern == null) { throw new ArgumentNullException(nameof(pattern)); } if (handler == null) { throw new ArgumentNullException(nameof(handler)); } var conventionBuilder = endpoints.Map(pattern, handler); conventionBuilder.WithDisplayName("Fallback " + pattern); conventionBuilder.Add(b => ((RouteEndpointBuilder)b).Order = int.MaxValue); return conventionBuilder; } private static RouteHandlerBuilder Map( this IEndpointRouteBuilder endpoints, RoutePattern pattern, Delegate handler, bool disableInferBodyFromParameters) { if (endpoints is null) { throw new ArgumentNullException(nameof(endpoints)); } if (pattern is null) { throw new ArgumentNullException(nameof(pattern)); } if (handler is null) { throw new ArgumentNullException(nameof(handler)); } const int defaultOrder = 0; var routeParams = new List<string>(pattern.Parameters.Count); foreach (var part in pattern.Parameters) { routeParams.Add(part.Name); } var routeHandlerOptions = endpoints.ServiceProvider?.GetService<IOptions<RouteHandlerOptions>>(); var options = new RequestDelegateFactoryOptions { ServiceProvider = endpoints.ServiceProvider, RouteParameterNames = routeParams, ThrowOnBadRequest = routeHandlerOptions?.Value.ThrowOnBadRequest ?? false, DisableInferBodyFromParameters = disableInferBodyFromParameters, }; var requestDelegateResult = RequestDelegateFactory.Create(handler, options); var builder = new RouteEndpointBuilder( requestDelegateResult.RequestDelegate, pattern, defaultOrder) { DisplayName = pattern.RawText ?? pattern.DebuggerToString(), }; // REVIEW: Should we add an IActionMethodMetadata with just MethodInfo on it so we are // explicit about the MethodInfo representing the "handler" and not the RequestDelegate? // Add MethodInfo as metadata to assist with OpenAPI generation for the endpoint.  builder.Metadata.Add(handler.Method); // Methods defined in a top-level program are generated as statics so the delegate // target will be null. Inline lambdas are compiler generated method so they can // be filtered that way. if (GeneratedNameParser.TryParseLocalFunctionName(handler.Method.Name, out var endpointName) || !TypeHelper.IsCompilerGeneratedMethod(handler.Method)) { endpointName ??= handler.Method.Name; builder.DisplayName = $"{builder.DisplayName} => {endpointName}"; } // Add delegate attributes as metadata var attributes = handler.Method.GetCustomAttributes(); // Add add request delegate metadata foreach (var metadata in requestDelegateResult.EndpointMetadata) { builder.Metadata.Add(metadata); } // This can be null if the delegate is a dynamic method or compiled from an expression tree if (attributes is not null) { foreach (var attribute in attributes) { builder.Metadata.Add(attribute); } } var dataSource = endpoints.DataSources.OfType<ModelEndpointDataSource>().FirstOrDefault(); if (dataSource is null) { dataSource = new ModelEndpointDataSource(); endpoints.DataSources.Add(dataSource); } return new RouteHandlerBuilder(dataSource.AddEndpointBuilder(builder)); } } }
*/

View Code

在这里我们重点看看下面签名的方法:

代码语言:javascript
复制
 /// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns> public static IEndpointConventionBuilder Map( this IEndpointRouteBuilder endpoints, RoutePattern pattern, RequestDelegate requestDelegate) { if (endpoints == null) { throw new ArgumentNullException(nameof(endpoints)); } if (pattern == null) { throw new ArgumentNullException(nameof(pattern)); } if (requestDelegate == null) { throw new ArgumentNullException(nameof(requestDelegate)); } const int defaultOrder = 0; //实例化RouteEndpointBuilder var builder = new RouteEndpointBuilder( requestDelegate, pattern, defaultOrder) { DisplayName = pattern.RawText ?? pattern.DebuggerToString(), }; // Add delegate attributes as metadata var attributes = requestDelegate.Method.GetCustomAttributes(); // This can be null if the delegate is a dynamic method or compiled from an expression tree if (attributes != null) { foreach (var attribute in attributes) { builder.Metadata.Add(attribute); } } //Adds a RouteEndpoint to the IEndpointRouteBuilder that matches HTTP requests /// for the specified pattern. ///可以理解为:将某种格式的路由pattern添加至IEndpointRouteBuilder的DataSource属性,为match做准备  var dataSource = endpoints.DataSources.OfType<ModelEndpointDataSource>().FirstOrDefault(); if (dataSource == null) { dataSource = new ModelEndpointDataSource(); endpoints.DataSources.Add(dataSource); } return dataSource.AddEndpointBuilder(builder); }

View Code

在这个方法里有一个实例化RouteEndpointBuilder的语句:

代码语言:javascript
复制
 var builder = new RouteEndpointBuilder( requestDelegate, pattern, defaultOrder) { DisplayName = pattern.RawText ?? pattern.DebuggerToString(), };

其中RouteEndpointBuilder从字面意思看得出,就是RouteEndpoint的创建者,我们看下它的代码:

代码语言:javascript
复制
 public sealed class RouteEndpointBuilder : EndpointBuilder { /// <summary> /// Gets or sets the <see cref="RoutePattern"/> associated with this endpoint. /// </summary> public RoutePattern RoutePattern { get; set; } /// <summary> /// Gets or sets the order assigned to the endpoint. /// </summary> public int Order { get; set; } /// <summary> /// Constructs a new <see cref="RouteEndpointBuilder"/> instance. /// </summary> /// <param name="requestDelegate">The delegate used to process requests for the endpoint.</param> /// <param name="routePattern">The <see cref="RoutePattern"/> to use in URL matching.</param> /// <param name="order">The order assigned to the endpoint.</param> public RouteEndpointBuilder( RequestDelegate requestDelegate, RoutePattern routePattern, int order) { RequestDelegate = requestDelegate; RoutePattern = routePattern; Order = order; } //生成Route /// <inheritdoc /> public override Endpoint Build() { if (RequestDelegate is null) { throw new InvalidOperationException($"{nameof(RequestDelegate)} must be specified to construct a {nameof(RouteEndpoint)}."); } var routeEndpoint = new RouteEndpoint( RequestDelegate, RoutePattern, Order, new EndpointMetadataCollection(Metadata), DisplayName); return routeEndpoint; } }

View Code

再来看下Endpoint的代码:

代码语言:javascript
复制
 /// <summary> /// Represents a logical endpoint in an application. /// </summary> public class Endpoint { /// <summary> /// Creates a new instance of <see cref="Endpoint"/>. /// </summary> /// <param name="requestDelegate">The delegate used to process requests for the endpoint.</param> /// <param name="metadata"> /// The endpoint <see cref="EndpointMetadataCollection"/>. May be null. /// </param> /// <param name="displayName"> /// The informational display name of the endpoint. May be null. /// </param> public Endpoint( RequestDelegate? requestDelegate, EndpointMetadataCollection? metadata, string? displayName) { // All are allowed to be null RequestDelegate = requestDelegate; Metadata = metadata ?? EndpointMetadataCollection.Empty; DisplayName = displayName; } /// <summary> /// Gets the informational display name of this endpoint. /// </summary> public string? DisplayName { get; } /// <summary> /// Gets the collection of metadata associated with this endpoint. /// </summary> public EndpointMetadataCollection Metadata { get; } /// <summary> /// Gets the delegate used to process requests for the endpoint. /// </summary> public RequestDelegate? RequestDelegate { get; } /// <summary> /// Returns a string representation of the endpoint. /// </summary> public override string? ToString() => DisplayName ?? base.ToString(); }

View Code

从Build()方法看得出,它利用路由匹配的RoutePattern,生成一个RouteEndpoint实例。RouteEndpoint叫做路由终点,继承自Endpoint。Endpoint有一个重要的RequestDelegate属性,用来处理当前的请求。看到这里,我们开始推断:所谓的Endpoint,无非就是用要匹配的pattern,构造一个RouteEndpoint的过程,其中RouteEndpoint继承自Endpoint。NetCore无非就是利用这个RouteEndpoint来匹配当前的Url,如果匹配得上,就执行RequestDelegate所代表的方法,上文就是WeatherForecast方法,如果匹配不上,则不执行WeatherForecast方法,仅此而已。

2.为了验证,我们再来看 app.UseRouting().UseEndpoints(),实际上调用的是EndpointRoutingApplicationBuilderExtensions类的两个方法:

代码语言:javascript
复制
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释 
* public static class EndpointRoutingApplicationBuilderExtensions { private const string EndpointRouteBuilder = "__EndpointRouteBuilder"; private const string GlobalEndpointRouteBuilderKey = "__GlobalEndpointRouteBuilder"; /// <summary> /// Adds a <see cref="EndpointRoutingMiddleware"/> middleware to the specified <see cref="IApplicationBuilder"/>. /// </summary> /// <param name="builder">The <see cref="IApplicationBuilder"/> to add the middleware to.</param> /// <returns>A reference to this instance after the operation has completed.</returns> /// <remarks> /// <para> /// A call to <see cref="UseRouting(IApplicationBuilder)"/> must be followed by a call to /// <see cref="UseEndpoints(IApplicationBuilder, Action{IEndpointRouteBuilder})"/> for the same <see cref="IApplicationBuilder"/> /// instance. /// </para> /// <para> /// The <see cref="EndpointRoutingMiddleware"/> defines a point in the middleware pipeline where routing decisions are /// made, and an <see cref="Endpoint"/> is associated with the <see cref="HttpContext"/>. The <see cref="EndpointMiddleware"/> /// defines a point in the middleware pipeline where the current <see cref="Endpoint"/> is executed. Middleware between /// the <see cref="EndpointRoutingMiddleware"/> and <see cref="EndpointMiddleware"/> may observe or change the /// <see cref="Endpoint"/> associated with the <see cref="HttpContext"/>. /// </para> /// </remarks> public static IApplicationBuilder UseRouting(this IApplicationBuilder builder) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } VerifyRoutingServicesAreRegistered(builder); IEndpointRouteBuilder endpointRouteBuilder; if (builder.Properties.TryGetValue(GlobalEndpointRouteBuilderKey, out var obj)) { endpointRouteBuilder = (IEndpointRouteBuilder)obj!; // Let interested parties know if UseRouting() was called while a global route builder was set builder.Properties[EndpointRouteBuilder] = endpointRouteBuilder; } else { endpointRouteBuilder = new DefaultEndpointRouteBuilder(builder); builder.Properties[EndpointRouteBuilder] = endpointRouteBuilder; } //先交由EndpointRoutingMiddleware中间件匹配Endpoint,然后交由下面的EndpointMiddleware处理 return builder.UseMiddleware<EndpointRoutingMiddleware>(endpointRouteBuilder); } /// <summary> /// Adds a <see cref="EndpointMiddleware"/> middleware to the specified <see cref="IApplicationBuilder"/> /// with the <see cref="EndpointDataSource"/> instances built from configured <see cref="IEndpointRouteBuilder"/>. /// The <see cref="EndpointMiddleware"/> will execute the <see cref="Endpoint"/> associated with the current /// request. /// </summary> /// <param name="builder">The <see cref="IApplicationBuilder"/> to add the middleware to.</param> /// <param name="configure">An <see cref="Action{IEndpointRouteBuilder}"/> to configure the provided <see cref="IEndpointRouteBuilder"/>.</param> /// <returns>A reference to this instance after the operation has completed.</returns> /// <remarks> /// <para> /// A call to <see cref="UseEndpoints(IApplicationBuilder, Action{IEndpointRouteBuilder})"/> must be preceded by a call to /// <see cref="UseRouting(IApplicationBuilder)"/> for the same <see cref="IApplicationBuilder"/> /// instance. /// </para> /// <para> /// The <see cref="EndpointRoutingMiddleware"/> defines a point in the middleware pipeline where routing decisions are /// made, and an <see cref="Endpoint"/> is associated with the <see cref="HttpContext"/>. The <see cref="EndpointMiddleware"/> /// defines a point in the middleware pipeline where the current <see cref="Endpoint"/> is executed. Middleware between /// the <see cref="EndpointRoutingMiddleware"/> and <see cref="EndpointMiddleware"/> may observe or change the /// <see cref="Endpoint"/> associated with the <see cref="HttpContext"/>. /// </para> /// </remarks> public static IApplicationBuilder UseEndpoints(this IApplicationBuilder builder, Action<IEndpointRouteBuilder> configure) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (configure == null) { throw new ArgumentNullException(nameof(configure)); } VerifyRoutingServicesAreRegistered(builder); VerifyEndpointRoutingMiddlewareIsRegistered(builder, out var endpointRouteBuilder); configure(endpointRouteBuilder); // Yes, this mutates an IOptions. We're registering data sources in a global collection which // can be used for discovery of endpoints or URL generation. // // Each middleware gets its own collection of data sources, and all of those data sources also // get added to a global collection. var routeOptions = builder.ApplicationServices.GetRequiredService<IOptions<RouteOptions>>(); foreach (var dataSource in endpointRouteBuilder.DataSources) { if (!routeOptions.Value.EndpointDataSources.Contains(dataSource)) { routeOptions.Value.EndpointDataSources.Add(dataSource); } } //交由EndpointMiddleware处理 return builder.UseMiddleware<EndpointMiddleware>(); } private static void VerifyRoutingServicesAreRegistered(IApplicationBuilder app) { // Verify if AddRouting was done before calling UseEndpointRouting/UseEndpoint // We use the RoutingMarkerService to make sure if all the services were added. if (app.ApplicationServices.GetService(typeof(RoutingMarkerService)) == null) { throw new InvalidOperationException(Resources.FormatUnableToFindServices( nameof(IServiceCollection), nameof(RoutingServiceCollectionExtensions.AddRouting), "ConfigureServices(...)")); } } private static void VerifyEndpointRoutingMiddlewareIsRegistered(IApplicationBuilder app, out IEndpointRouteBuilder endpointRouteBuilder) { if (!app.Properties.TryGetValue(EndpointRouteBuilder, out var obj)) { var message = $"{nameof(EndpointRoutingMiddleware)} matches endpoints setup by {nameof(EndpointMiddleware)} and so must be added to the request " + $"execution pipeline before {nameof(EndpointMiddleware)}. " + $"Please add {nameof(EndpointRoutingMiddleware)} by calling '{nameof(IApplicationBuilder)}.{nameof(UseRouting)}' inside the call " + $"to 'Configure(...)' in the application startup code."; throw new InvalidOperationException(message); } endpointRouteBuilder = (IEndpointRouteBuilder)obj!; // This check handles the case where Map or something else that forks the pipeline is called between the two // routing middleware. if (endpointRouteBuilder is DefaultEndpointRouteBuilder defaultRouteBuilder && !object.ReferenceEquals(app, defaultRouteBuilder.ApplicationBuilder)) { var message = $"The {nameof(EndpointRoutingMiddleware)} and {nameof(EndpointMiddleware)} must be added to the same {nameof(IApplicationBuilder)} instance. " + $"To use Endpoint Routing with 'Map(...)', make sure to call '{nameof(IApplicationBuilder)}.{nameof(UseRouting)}' before " + $"'{nameof(IApplicationBuilder)}.{nameof(UseEndpoints)}' for each branch of the middleware pipeline."; throw new InvalidOperationException(message); } } }
*/

View Code

2-1首先看UseRouting()方法,这个方法其实就是调用EndpointRoutingMiddleware中间件进行Endpoint的匹配,我们可以看下它的几个方法:

代码语言:javascript
复制
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释 
* internal sealed partial class EndpointRoutingMiddleware { private const string DiagnosticsEndpointMatchedKey = "Microsoft.AspNetCore.Routing.EndpointMatched"; private readonly MatcherFactory _matcherFactory; private readonly ILogger _logger; private readonly EndpointDataSource _endpointDataSource; private readonly DiagnosticListener _diagnosticListener; private readonly RequestDelegate _next; private Task<Matcher>? _initializationTask; public EndpointRoutingMiddleware( MatcherFactory matcherFactory, ILogger<EndpointRoutingMiddleware> logger, IEndpointRouteBuilder endpointRouteBuilder, DiagnosticListener diagnosticListener, RequestDelegate next) { if (endpointRouteBuilder == null) { throw new ArgumentNullException(nameof(endpointRouteBuilder)); } _matcherFactory = matcherFactory ?? throw new ArgumentNullException(nameof(matcherFactory)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _diagnosticListener = diagnosticListener ?? throw new ArgumentNullException(nameof(diagnosticListener)); _next = next ?? throw new ArgumentNullException(nameof(next)); _endpointDataSource = new CompositeEndpointDataSource(endpointRouteBuilder.DataSources); } public Task Invoke(HttpContext httpContext) { // There's already an endpoint, skip matching completely var endpoint = httpContext.GetEndpoint(); if (endpoint != null) { Log.MatchSkipped(_logger, endpoint); return _next(httpContext); } // There's an inherent race condition between waiting for init and accessing the matcher // this is OK because once `_matcher` is initialized, it will not be set to null again. var matcherTask = InitializeAsync(); if (!matcherTask.IsCompletedSuccessfully) { return AwaitMatcher(this, httpContext, matcherTask); } var matchTask = matcherTask.Result.MatchAsync(httpContext); if (!matchTask.IsCompletedSuccessfully) { return AwaitMatch(this, httpContext, matchTask); } return SetRoutingAndContinue(httpContext); // Awaited fallbacks for when the Tasks do not synchronously complete static async Task AwaitMatcher(EndpointRoutingMiddleware middleware, HttpContext httpContext, Task<Matcher> matcherTask) { var matcher = await matcherTask; //根据httpContext进行匹配 await matcher.MatchAsync(httpContext); await middleware.SetRoutingAndContinue(httpContext); } static async Task AwaitMatch(EndpointRoutingMiddleware middleware, HttpContext httpContext, Task matchTask) { await matchTask; await middleware.SetRoutingAndContinue(httpContext); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private Task SetRoutingAndContinue(HttpContext httpContext) { // If there was no mutation of the endpoint then log failure var endpoint = httpContext.GetEndpoint(); if (endpoint == null) { Log.MatchFailure(_logger); } else { // Raise an event if the route matched if (_diagnosticListener.IsEnabled() && _diagnosticListener.IsEnabled(DiagnosticsEndpointMatchedKey)) { // We're just going to send the HttpContext since it has all of the relevant information  _diagnosticListener.Write(DiagnosticsEndpointMatchedKey, httpContext); } Log.MatchSuccess(_logger, endpoint); } return _next(httpContext); } // Initialization is async to avoid blocking threads while reflection and things // of that nature take place. // // We've seen cases where startup is very slow if we allow multiple threads to race // while initializing the set of endpoints/routes. Doing CPU intensive work is a // blocking operation if you have a low core count and enough work to do. private Task<Matcher> InitializeAsync() { var initializationTask = _initializationTask; if (initializationTask != null) { return initializationTask; } return InitializeCoreAsync(); } private Task<Matcher> InitializeCoreAsync() { var initialization = new TaskCompletionSource<Matcher>(TaskCreationOptions.RunContinuationsAsynchronously); var initializationTask = Interlocked.CompareExchange(ref _initializationTask, initialization.Task, null); if (initializationTask != null) { // This thread lost the race, join the existing task. return initializationTask; } // This thread won the race, do the initialization. try { //用EndpointDataSource初始化matcher //EndpointDataSource来源于IEndpointRouteBuilder的DataSource属性 //见EndpointRouteBuilderExtensions类的201行 var matcher = _matcherFactory.CreateMatcher(_endpointDataSource); _initializationTask = Task.FromResult(matcher); // Complete the task, this will unblock any requests that came in while initializing.  initialization.SetResult(matcher); return initialization.Task; } catch (Exception ex) { // Allow initialization to occur again. Since DataSources can change, it's possible // for the developer to correct the data causing the failure. _initializationTask = null; // Complete the task, this will throw for any requests that came in while initializing.  initialization.SetException(ex); return initialization.Task; } } private static partial class Log { public static void MatchSuccess(ILogger logger, Endpoint endpoint) => MatchSuccess(logger, endpoint.DisplayName); [LoggerMessage(1, LogLevel.Debug, "Request matched endpoint '{EndpointName}'", EventName = "MatchSuccess")] private static partial void MatchSuccess(ILogger logger, string? endpointName); [LoggerMessage(2, LogLevel.Debug, "Request did not match any endpoints", EventName = "MatchFailure")] public static partial void MatchFailure(ILogger logger); public static void MatchSkipped(ILogger logger, Endpoint endpoint) => MatchingSkipped(logger, endpoint.DisplayName); [LoggerMessage(3, LogLevel.Debug, "Endpoint '{EndpointName}' already set, skipping route matching.", EventName = "MatchingSkipped")] private static partial void MatchingSkipped(ILogger logger, string? endpointName); } }
*/

View Code

从Invoke方法看得出来,它根据当前的HttpContext进行Endpoint的匹配,如果当前的HttpContext路由格式匹配成功,那么将当前HttpContext传递给下一个中间件处理,这个从SetRoutingAndContinue方法看得出来。

2-2其次看下UseEndpoints()方法,这个方法就是调用EndpointMiddleware中间件,对上面匹配成功的HttpContext进行处理,并调用HttpContext的EndPoint的RequestDelegate处理当前请求。我们重点看下它的Invoke方法,重点关注var requestTask = endpoint.RequestDelegate(httpContext):

代码语言:javascript
复制
 public Task Invoke(HttpContext httpContext) { var endpoint = httpContext.GetEndpoint(); if (endpoint?.RequestDelegate != null) { if (!_routeOptions.SuppressCheckForUnhandledSecurityMetadata) { if (endpoint.Metadata.GetMetadata<IAuthorizeData>() != null && !httpContext.Items.ContainsKey(AuthorizationMiddlewareInvokedKey)) { ThrowMissingAuthMiddlewareException(endpoint); } if (endpoint.Metadata.GetMetadata<ICorsMetadata>() != null && !httpContext.Items.ContainsKey(CorsMiddlewareInvokedKey)) { ThrowMissingCorsMiddlewareException(endpoint); } } Log.ExecutingEndpoint(_logger, endpoint); try { //调用HttpContext的Endpoint的RequestDelegate方法处理当前请求 var requestTask = endpoint.RequestDelegate(httpContext); if (!requestTask.IsCompletedSuccessfully) { return AwaitRequestTask(endpoint, requestTask, _logger); } } catch (Exception exception) { Log.ExecutedEndpoint(_logger, endpoint); return Task.FromException(exception); } Log.ExecutedEndpoint(_logger, endpoint); return Task.CompletedTask; } return _next(httpContext); static async Task AwaitRequestTask(Endpoint endpoint, Task requestTask, ILogger logger) { try { await requestTask; } finally { Log.ExecutedEndpoint(logger, endpoint); } } }

View Code

总结:从上面的分析,我们粗略的了解了netcore路由的Endpoint模式其实就是一种用匹配模式构建的终端节点,它主要用来对HttpContext进行路由的匹配,如果匹配成功,则执行Endpoint上的RequestDelegate方法。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/170755.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档