项目作者: sulmar

项目描述 :
Przykład definiowania tras za pomocą endpoints
高级语言: C#
项目地址: git://github.com/sulmar/dotnet-core-endpoints-routing.git
创建时间: 2020-02-07T09:14:19Z
项目社区:https://github.com/sulmar/dotnet-core-endpoints-routing

开源协议:

下载


Endpoints w .NET Core

Wprowadzenie

W .NET Core 2.2 wprowadzony został mechanizm endpoints a w .NET Core 3 stał się zalecanym sposobem mapowania żądań.
Korzystają z niego chociażby technologie MVC i SignalR.

Czyli obecnie zamiast:

  1. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  2. {
  3. app.UseMvc();
  4. }

Piszemy:

  1. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  2. {
  3. app.UseEndpoints(endpoints =>
  4. {
  5. endpoints.MapControllerRoute(
  6. name: "default",
  7. pattern: "{controller=Home}/{action=Index}/{id?}");
  8. });
  9. }

Dlaczego to zostało zmienione?

Otóż wcześniej każdy middleware miał własny sposób mapowania ścieżek, na przykład UseMvc(), UseSignalR()
To powodowało, że każdy framework był konfigurowany w nieco w inny sposób.

Dzięki endpoints zostało to zunifikowane i teraz każdy programista może skorzystać z tego mechanizmu podczas tworzenia własnej warstwy pośredniej (middleware).

W takim razie w jaki sposób zastosować to we własnym rozwiązaniu?

Wymaganie

Chcemy utworzyć własny dasboard, który będzie podpinany pod url /mydashoard

  1. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  2. {
  3. app.UseRouting();
  4. app.UseEndpoints(endpoints =>
  5. {
  6. endpoints.MapMyDashboard("/mydashboard");
  7. }
  8. }

Warstwa pośrednia (middleware)

Na początek utwórzmy warstwę pośrednią

  1. public class MyDashboardMiddleware
  2. {
  3. private readonly RequestDelegate next;
  4. public MyDashboardMiddleware(RequestDelegate next)
  5. {
  6. this.next = next;
  7. }
  8. public async Task InvokeAsync(HttpContext context)
  9. {
  10. string title = "My dashboard";
  11. string content = "Hello World!";
  12. context.Response.StatusCode = 200;
  13. context.Response.ContentType = "text/html";
  14. await context.Response.WriteAsync($@"<html><head><title>{title}</title><head><body>{content}</body></html>");
  15. }
  16. }

Użycie

Teraz możemy podpiąć w klasie Startup z użyciem endpoints:

  1. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  2. {
  3. if (env.IsDevelopment())
  4. {
  5. app.UseDeveloperExceptionPage();
  6. }
  7. app.UseRouting();
  8. app.UseEndpoints(endpoints =>
  9. {
  10. endpoints.Map("/", async context => await context.Response.WriteAsync("Hello World!"));
  11. endpoints.Map("/mydashboard", endpoints.CreateApplicationBuilder()
  12. .UseMiddleware<MyDashboardMiddleware>()
  13. .Build());
  14. }

Utworzenie metody rozszerzającej

W celu ułatwienia korzystania z naszej warstwy utworzymy metodę rozszerzającą MapMyDashboard()

  1. public static class MyEndpointRouteBuilderExtensions
  2. {
  3. public static IEndpointConventionBuilder MapMyDashboard(
  4. this IEndpointRouteBuilder endpoints,
  5. string pattern = "/dashboard")
  6. {
  7. var app = endpoints.CreateApplicationBuilder();
  8. var pipeline = app
  9. .UsePathBase(pattern)
  10. .UseMiddleware<MyDashboardMiddleware>()
  11. .Build();
  12. // Glob patterns
  13. // https://docs.microsoft.com/pl-pl/aspnet/core/fundamentals/file-providers?view=aspnetcore-3.1
  14. return endpoints.Map(pattern + "/{**path}", pipeline);
  15. }
  16. }

Zastosowanie metody MapMyDashboard()

  1. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  2. {
  3. app.UseRouting();
  4. app.UseEndpoints(endpoints =>
  5. {
  6. endpoints.Map("/", async context => await context.Response.WriteAsync("Hello World!"));
  7. endpoints.MapMyDashboard("/mydashboard");
  8. }
  9. }

Konfiguracja

W jaki sposób przekazać opcje na wzór MapHub?

Opcje

Tworzymy klasę opcji:

  1. public class MyDashboardOptions
  2. {
  3. public string DashboardTitle { get; set; }
  4. }

Warstwa pośrednia (middleware)

Przekazujemy ją poprzez konstruktor

  1. public class MyDashboardMiddleware
  2. {
  3. private readonly RequestDelegate next;
  4. private readonly MyDashboardOptions options;
  5. public MyDashboardMiddleware(RequestDelegate next, MyDashboardOptions options)
  6. {
  7. this.next = next;
  8. this.options = options;
  9. }
  10. public async Task InvokeAsync(HttpContext context)
  11. {
  12. string title = options.DashboardTitle;
  13. string content = "Hello World!";
  14. context.Response.StatusCode = 200;
  15. context.Response.ContentType = "text/html";
  16. await context.Response.WriteAsync($@"<html><head><title>{title}</title><head><body>{content}</body></html>");
  17. }
  18. }

Metoda rozszerzająca

Dodajemy parametr Action gdzie T to nasza klasa z opcjami:

  1. public static class MyEndpointRouteBuilderExtensions
  2. {
  3. public static IEndpointConventionBuilder MapMyDashboard(
  4. this IEndpointRouteBuilder endpoints,
  5. string pattern = "/dashboard",
  6. Action<MyDashboardOptions> configureOptions = null
  7. )
  8. {
  9. var app = endpoints.CreateApplicationBuilder();
  10. var options = new MyDashboardOptions();
  11. configureOptions?.Invoke(options);
  12. var pipeline = app
  13. .UsePathBase(pattern)
  14. .UseMiddleware<MyDashboardMiddleware>(options)
  15. .Build();
  16. // Glob patterns
  17. // https://docs.microsoft.com/pl-pl/aspnet/core/fundamentals/file-providers?view=aspnetcore-3.1
  18. return endpoints.Map(pattern + "/{**path}", pipeline);
  19. }

Użycie

  1. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  2. {
  3. if (env.IsDevelopment())
  4. {
  5. app.UseDeveloperExceptionPage();
  6. }
  7. app.UseRouting();
  8. app.UseEndpoints(endpoints =>
  9. {
  10. endpoints.MapMyDashboard("/mydashboard", options => options.DashboardTitle = "My dashboard");
  11. }
  12. }

Wstrzykiwanie zależności (Dependency Injections)

Utworzenie usługi

  1. public interface IContentService
  2. {
  3. string Get();
  4. }
  5. public class MyContentService : IContentService
  6. {
  7. public string Get()
  8. {
  9. return "Hello Dashboard!";
  10. }
  11. }
  12. public static class MyServiceCollectionExtensions
  13. {
  14. public static IServiceCollection AddMyContentService([NotNull] this IServiceCollection services)
  15. {
  16. services.AddTransient<IContentService, MyContentService>();
  17. return services;
  18. }
  19. }

Utworzenie warstwy pośredniej (middleware)

  1. public class MyDashboardMiddleware
  2. {
  3. private readonly RequestDelegate next;
  4. private readonly MyDashboardOptions options;
  5. private readonly IContentService contentService;
  6. public MyDashboardMiddleware(RequestDelegate next, MyDashboardOptions options, IContentService contentService)
  7. {
  8. this.next = next;
  9. this.options = options;
  10. this.contentService = contentService;
  11. }
  12. public async Task InvokeAsync(HttpContext context)
  13. {
  14. string content = contentService.Get();
  15. context.Response.StatusCode = 200;
  16. context.Response.ContentType = "text/html";
  17. await context.Response.WriteAsync($@"<html><head><title>{options.DashboardTitle}</title><head><body>{content}</body></html>");
  18. }
  19. }

Utworzenie metody rozszerzającej

  1. public static class MyServiceCollectionExtensions
  2. {
  3. public static IServiceCollection AddMyContentService([NotNull] this IServiceCollection services)
  4. {
  5. services.AddTransient<IContentService, MyContentService>();
  6. return services;
  7. }
  8. }

Użycie

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddMyContentService();
  4. }

Podsumowanie

Przedstawiony kod można potraktować jako szablon do tworzenia własnych rozwiązań.

Powstał w oparciu o analizę kodu źródłowego Signal-R
https://github.com/dotnet/aspnetcore/tree/master/src/SignalR/server/SignalR/src
a w szczególności klasy
https://github.com/dotnet/aspnetcore/blob/master/src/SignalR/server/SignalR/src/HubEndpointRouteBuilderExtensions.cs