{"id":633,"date":"2025-04-05T11:43:31","date_gmt":"2025-04-05T03:43:31","guid":{"rendered":"https:\/\/www.hyy.net\/?p=633"},"modified":"2025-04-05T11:43:31","modified_gmt":"2025-04-05T03:43:31","slug":"asp-net-core-in-action-31-advanced-con%ef%ac%81guration-of-asp-net-core","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=633","title":{"rendered":"ASP.NET Core in Action 31 Advanced con\ufb01guration of ASP.NET Core"},"content":{"rendered":"<h1>31 Advanced con\ufb01guration of ASP.NET Core\u200c<\/h1>\n<h2>This chapter covers<\/h2>\n<p>\u2022  Building custom middleware<br \/>\n\u2022  Using dependency injection (DI) services in IOptions con\ufb01guration<br \/>\n\u2022  Replacing the built-in DI container with a third-party container<\/p>\n<p>When you\u2019re building apps with ASP.NET Core, most of your creativity and specialization go into the services and models that make up your business logic and the Razor Pages and APIs that expose them. Eventually, however, you\u2019re likely to \ufb01nd that you can\u2019t quite achieve a desired feature using the components that come out of the box. At that point, you may need to look to more complex uses of the built- in features.<\/p>\n<p>This chapter shows some of the ways you can customize cross-cutting parts of your application, such as your DI container or your middleware pipeline. These approaches are particularly useful if you\u2019re coming from a legacy application or are working on an existing project, and you want to continue to use the patterns and libraries you\u2019re familiar with.<\/p>\n<p>We\u2019ll start by looking at the middleware pipeline. You saw how to build pipelines by piecing together existing<\/p>\n<p>middleware in chapter 4, but in this chapter you\u2019ll create your own custom middleware. You\u2019ll explore the basic middleware constructs of the Map, Use, and Run methods and learn how to create standalone middleware classes.\u200c<\/p>\n<p>You\u2019ll use these to build middleware components that can add headers to all your responses as well as middleware that returns responses. Finally, you\u2019ll learn how to turn your custom middleware into a simple endpoint, using endpoint routing.<\/p>\n<p>In chapter 10 you learned about strongly typed con\ufb01guration using the <code>IOptions&lt;T&gt;<\/code> pattern, and in section 31.2 you\u2019ll learn how to take this further. You\u2019ll learn how to use the <code>OptionsBuilder&lt;T&gt;<\/code> type to \ufb02uently build your <code>IOptions&lt;T&gt;<\/code> object with the builder pattern. You\u2019ll also see how to use services from DI when con\ufb01guring your IOptions objects\u2014something that\u2019s not possible using the methods you\u2019ve seen so far.<\/p>\n<p>We stick with DI in section 31.3, where I\u2019ll show you how to replace the built-in DI container with a third-party alternative. The built-in container is \ufb01ne for most small apps, but your ConfigureServices function can quickly get bloated as your app grows and you register more services.<\/p>\n<p>I\u2019ll show you how to integrate the third-party Lamar library into an existing app, so you can use extra features such as automatic service registration by convention.<\/p>\n<p>The components and techniques shown in this chapter are more advanced than most features you\u2019ve seen so far. You likely won\u2019t need them in every ASP.NET Core project, but they\u2019re good to have in your back pocket should the need arise!<\/p>\n<h2>31.1 Customizing your middleware pipeline\u200c<\/h2>\n<p>In this section you\u2019ll learn how to create custom middleware. You\u2019ll learn how to use the Map, Run, and Use extension methods to create simple middleware using lambda expressions. You\u2019ll then see how to create equivalent middleware components using dedicated classes. You\u2019ll also learn how to split the middleware pipeline into branches, and you\u2019ll \ufb01nd out when this is useful.<\/p>\n<p>The middleware pipeline is one of the building blocks of ASP.NET Core apps, so we covered it in depth in chapter 4. Every request passes through the middleware pipeline, and each middleware component in turn gets an opportunity to modify the request or to handle it and return a response.<\/p>\n<p>ASP.NET Core includes middleware for handling common scenarios out of the box. You\u2019ll \ufb01nd middleware for serving static \ufb01les, handling errors, authentication, and many more tasks.<\/p>\n<p>You\u2019ll spend most of your time during development working with Razor Pages, minimal API endpoints, or web API controllers. These are exposed as the endpoints for most of your app\u2019s business logic, and they call methods on your app\u2019s various business services and models. However, you\u2019ve also seen middleware like the Swagger middleware and the WelcomePageMiddleware that returns a response without using the endpoint routing system. The various improvements to the routing system in .NET 7 mean I rarely \ufb01nd the need to create \u201cterminal\u201d middleware like this, as endpoint routing is easy to work with and extensible.Nevertheless, it may occasionally be preferable to create small, custom, terminal middleware components like these.<\/p>\n<p>At other times, you might have requirements that lie outside the remit of Razor Pages or minimal API endpoints. For example, you might want to ensure that all responses generated by your app include a speci\ufb01c header. This sort of cross-cutting concern is a perfect \ufb01t for custom middleware. You could add the custom middleware early in your middleware pipeline to ensure that every response from your app includes the required header, whether it comes from the static-\ufb01le middleware, the error handling middleware, or a Razor Page.<\/p>\n<p>In this section I show three ways to create custom middleware components, as well as how to create branches in your middleware pipeline where a request can \ufb02ow down either one branch or another. By combining the methods demonstrated in this section, you\u2019ll be able to create custom solutions to handle your speci\ufb01c requirements.<\/p>\n<p>We start by creating a middleware component that returns the current time as plain text whenever the app receives a request. From there we\u2019ll look at branching the pipeline, creating general-purpose middleware components, and encapsulating your middleware into standalone classes.<\/p>\n<p>Finally, in section 31.1.5 you\u2019ll see how to turn your custom middleware component into an endpoint and integrate it with the endpoint routing system.<\/p>\n<h3>31.1.1 Creating simple apps with the Run extension\u200c<\/h3>\n<p>As you\u2019ve seen in previous chapters, you de\ufb01ne the middleware pipeline for your app in Program.cs by adding middleware to a WebApplication object, typically using extension methods, as in this example:\u200c<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nWebApplication app = builder.Build();\napp.UseExceptionHandler();\napp.UseStaticFiles();\napp.Run();<\/code><\/pre>\n<p>When your app receives a request, the request passes through each middleware component, each of which gets a chance to modify the request or handle it by generating a response. If a middleware component generates a response, it e\ufb00ectively short-circuits the pipeline; no subsequent middleware in the pipeline sees the request. The response passes back through the earlier middleware components on its way back to the browser.<\/p>\n<p>You can use the Run extension method to build a simple middleware component that always generates a response. This extension takes a single lambda function that runs whenever a request reaches the component. The Run extension always generates a response, so no middleware placed after it ever executes. For that reason, you should always place the Run middleware last in a middleware pipeline.<\/p>\n<p><b>TIP<\/b>  Remember that middleware components run in the order in which you add them to the pipeline. If a middleware component handles a request and generates a response, later middleware never sees the request.<\/p>\n<p>The Run extension method provides access to the request in the form of the HttpContext object you saw in chapter 4. This contains all the details of the request in the Request property, such as the URL path, the headers, and the body of the request. It also contains a Response property you can use to return a response.\u200c\u200c\u200c<\/p>\n<p>The following listing shows how you could build a simple middleware component that returns the current time. It uses the provided HttpContext context object and the Response property to set the Content-Type header of the response (not strictly necessary in this case, as text\/plain is used if an alternative content type is not set) and writes the body of the response using WriteAsync(text).<\/p>\n<p>Listing 31.1 Creating simple middleware using the Run extension<\/p>\n<pre><code>app.Run(async (HttpContext context) =&gt; \u2776\n{\ncontext.Response.ContentType = &quot;text\/plain&quot;; \u2777\nawait context.Response.WriteAsync( \u2778\nDateTimeOffset.UtcNow.ToString()); \u2778\n});\napp.UseStaticFiles(); \u2779<\/code><\/pre>\n<p>\u2776 Uses the Run extension to create simple middleware that always returns a response<br \/>\n\u2777 You should set the content-type of the response you\u2019re generating; text\/plain is the default value.<br \/>\n\u2778 Returns the time as a string in the response. The 200 OK status code is used if not explicitly set.<br \/>\n\u2779 Any middleware added after the Run extension will never execute.<\/p>\n<p>The Run extension is useful for two di\ufb00erent things:<\/p>\n<p>\u2022  Creating simple middleware that always generates a response<\/p>\n<p>\u2022  Creating complex middleware that hijacks the whole request to build an additional framework on top of ASP.NET Core<\/p>\n<p>Whether you\u2019re using the Run extension to create basic endpoints or a complex extra framework layer, the middleware always generates some sort of response.<\/p>\n<p>Therefore, you must always place it at the end of the pipeline, as no middleware placed after it will execute.<\/p>\n<p><b>TIP<\/b>  Using the Run extension to unconditionally generate a response is rare these days. The endpoint routing system used by minimal APIs provides many extra niceties such as model binding, routing, integration with other middleware such as authentication and authorization, and so on.<\/p>\n<p>There may be occasional situations where you want to unconditionally generate a response, but a more common scenario is where you want your middleware component to respond only to a speci\ufb01c URL path, such as the way the Swagger UI middleware responds only to the \/swagger path. In the next section you\u2019ll see how you can combine Run with the Map extension method to create branching middleware pipelines.<\/p>\n<h3>31.1.2 Branching middleware\u200c pipelines with the Map extension<\/h3>\n<p>So far when discussing the middleware pipeline, we\u2019ve always considered it to be a single pipeline of sequential components. Each request passes through every middleware component until one component generates a response; then the response passes back through the previous middleware.<\/p>\n<p>The Map extension method lets you change that simple pipeline into a branching structure. Each branch of the pipeline is independent; a request passes through one branch or the other but not both, as shown in \ufb01gure 31.1. The Map extension method looks at the path of the request\u2019s URL. If the path starts with the required pattern, the request travels down the branch of the pipeline; otherwise, it remains on the main trunk. This lets you have completely di\ufb00erent behavior in di\ufb00erent branches of your middleware pipeline.<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/3101.jpg\" alt=\"alt text\" \/><\/p>\n<p>Figure 31.1 A sequential middleware pipeline compared with a branching pipeline created with the Map extension. In branching middleware, requests pass through only one of the branches at most. Middleware on the other branch never see the request and aren\u2019t executed.<\/p>\n<p><b>NOTE<\/b>  The URL-matching used by Map is conceptually similar to the routing you\u2019ve seen throughout the book, but it is much more basic, with many limitations. For example, it uses a simple string-pre\ufb01x match, and you can\u2019t use route parameters. Generally, you should favor using endpoint routing instead of branching using Map. A similar extension, MapWhen, allows matching based on anything in HttpContext, such as headers or query string parameters.<\/p>\n<p>For example, imagine you want to add a simple health-check endpoint to your existing app. This endpoint is a simple URL you can call that indicates whether your app is running correctly. You could easily create a health-check middleware using the Run extension, as you saw in listing 31.1, but then that\u2019s all your app can do. You want the health-check to respond only to a speci\ufb01c URL, \/ping. Your Razor Pages should handle all other requests as normal.<\/p>\n<p><b>TIP<\/b> The health-check scenario is a simple example for demonstrating the Map method, but ASP.NET Core includes built-in support for health-check endpoints, which integrate into the endpoint routing system. You should use these instead of creating your own. You can learn more about creating health checks in Microsoft\u2019s \u201cHealth checks in ASP.NET Core\u201d documentation: <a href=\"http:\/\/mng.bz\/nMA2\">http:\/\/mng.bz\/nMA2<\/a>.<\/p>\n<p>One solution would be to create a branch using the Map extension method and to place the health-check middleware on that branch, as shown in \ufb01gure 31.1. Only those requests that match the Map pattern \/ping will execute the branch; all other requests are handled by the standard routing middleware and Razor Pages on the main trunk instead, as shown in the following listing.<\/p>\n<p>Listing 31.2 Using the Map extension to create branching middleware pipelines<\/p>\n<pre><code>app.UseStatusCodePages(); \u2776\napp.Map(&quot;\/ping&quot;, (IApplicationBuilder branch) =&gt; \u2777\n{\nbranch.UseExceptionHandler(); \u2778\nbranch.Run(async (HttpContext context) =&gt; \u2779\n{ \u2779\ncontext.Response.ContentType = &quot;text\/plain&quot;; \u2779\nawait context.Response.WriteAsync(&quot;pong&quot;); \u2779\n}); \u2779\n});\napp.UseStaticFiles(); \u277a\napp.UseRouting(); \u277a\napp.MapRazorPages(); \u277a\napp.Run();<\/code><\/pre>\n<p>\u2776 Every request passes through this middleware.<br \/>\n\u2777 The Map extension method branches if a request starts with \/ping.<br \/>\n\u2778 This middleware runs only for requests matching the \/ping branch.<br \/>\n\u2779 The Run extension always returns a response, but only on the \/ping branch.<br \/>\n\u277a The rest of the middleware pipeline run for requests that don\u2019t match the \/ping branch.<\/p>\n<p>The Map middleware creates a completely new IApplicationBuilder (called branch in the listing), which you can customize as you would your main app pipeline. Middleware added to the branch builder are added only to the branch pipeline, not the main trunk pipeline.\u200c<\/p>\n<p><b>TIP<\/b>  The WebApplication object you typically add middleware to implements the IApplicationBuilder interface. Most extension methods for adding middleware use the IApplicationBuilder interface, so you can use\u200c the extension methods in branches as well as your main middleware pipeline.<\/p>\n<p>In this example, you add the Run middleware to the branch, so it executes only for requests that start with \/ping, such as \/ping, \/ping\/go, and \/ping?id=123. Any requests that don\u2019t start with \/ping are ignored by the Map extension. Those requests stay on the main trunk pipeline and execute the next middleware in the pipeline after Map (in this case, the StaticFilesMiddleware).<\/p>\n<p><b>WARNING<\/b> There are several Map extension method overloads. Some of these are extension methods on IApplicationBuilder and are used to branch the pipeline, as you saw in listing 31.2. Other overloads are extensions on IEndpointRouteBuilder and are used to create minimal endpoints, using the endpoint routing system. If you\u2019re struggling to make your app compile, make sure that you\u2019re not accidentally using the wrong Map overload!<\/p>\n<p>If you need to, you can create sprawling branched pipelines using Map, where each branch is independent of every other. You could also nest calls to Map so you have branches coming o\ufb00 branches.<\/p>\n<p>The Map extension can be useful, but if you try to get too elaborate, it can quickly get confusing. Remember that you should use middleware for implementing cross-cutting concerns or simple endpoints. The endpoint routing mechanism of minimal APIs and Razor Pages is better suited to more complex routing requirements, so always favor it over Map where possible.<\/p>\n<p>One situation where Map can be useful is when you want to have two independent subapplications but don\u2019t want the hassle of multiple deployments. You can use Map to keep these pipelines separate, with separate routing and endpoints inside each branch of the pipeline.<\/p>\n<p><b>TIP<\/b> This approach can be useful, for example, if you\u2019re embedding an OpenID Connect server such as IdentityServer in your application. By mapping IdentityServer to a branch, you ensure that the endpoints and controllers in your main app can\u2019t interfere with the endpoints exposed by IdentityServer.<\/p>\n<p>Be aware that these branches share con\ufb01guration and a DI container, so they\u2019re independent only from the middleware pipeline\u2019s point of view. You must also remember that WebApplication adds lots of middleware to the pipeline by default, so you may need to override these by explicitly calling UseRouting() in all your branches, for example.<\/p>\n<p><b>NOTE<\/b>  Achieving truly independent branches in the same application requires a lot of e\ufb00ort. See Filip Wojcieszyn\u2019s blog post, \u201cRunning multiple independent ASP.NET Core pipelines side by side in the same application,\u201d for guidance: <a href=\"http:\/\/mng.bz\/vzA4\">http:\/\/mng.bz\/vzA4<\/a>.<\/p>\n<p>The \ufb01nal point you should be aware of when using the Map extension is that it modi\ufb01es the e\ufb00ective Path seen by middleware on the branch. When it matches a URL pre\ufb01x, the Map extension cuts o\ufb00 the matched segment from the path, as shown in \ufb01gure 31.2. The removed segments are stored on a property of HttpContext called PathBase, so they\u2019re still accessible if you need them.<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/3102.jpg\" alt=\"alt text\" \/><\/p>\n<p>Figure 31.2 When the Map extension diverts a request to a branch, it removes the matched segment from the Path property and adds it to the PathBase property.\u200c<\/p>\n<p><b>NOTE<\/b>  ASP.NET Core\u2019s link generator (used in Razor and minimal APIs, as discussed in chapter 6) uses PathBase to ensure that it generates URLs that include the PathBase as a pre\ufb01x.<\/p>\n<p>You\u2019ve seen the Run extension, which always returns a response, and the Map extension, which creates a branch in the pipeline. The next extension we\u2019ll look at is the general- purpose Use extension.<\/p>\n<p>31.1.3 Adding to the pipeline with the Use extension\u200c<\/p>\n<p>You can use the Use extension method to add a general- purpose piece of middleware. You can use it to view and modify requests as they arrive, to generate a response, or to pass the request on to subsequent middleware in the pipeline.<\/p>\n<p>As with the Run extension, when you add the Use extension to your pipeline, you specify a lambda function that runs when a request reaches the middleware. ASP.NET Core passes two parameters to this function:<\/p>\n<p>\u2022  The HttpContext representing the current request and response\u2014You can use this to inspect the request or generate a response, as you saw with the Run extension.<\/p>\n<p>\u2022  A pointer to the rest of the pipeline as a Func<Task>\u2014By executing this task, you can execute the rest of the middleware pipeline.<\/p>\n<p>By providing a pointer to the rest of the pipeline, you can use the Use extension to control exactly how and when the rest of the pipeline executes, as shown in \ufb01gure 31.3. If you don\u2019t call the provided Func<Task> at all, the rest of the pipeline doesn\u2019t execute for the request, so you have complete control.<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/3103.jpg\" alt=\"alt text\" \/><\/p>\n<p>Figure 31.3 Three pieces of middleware, created with the Use extension. Invoking the provided Func<Task> using next() invokes the rest of the pipeline. Each middleware component can run code before and after calling the rest of the pipeline, or it can choose to not call next() to short-circuit the pipeline.<\/p>\n<p>Exposing the rest of the pipeline as a Func<Task> makes it easy to conditionally short-circuit the pipeline, which enables<\/p>\n<p>many scenarios. Instead of branching the pipeline to implement the health-check middleware with Map and Run, as you did in listing 31.2, you could use a single instance of the Use extension, as shown in the following listing. This provides the same required functionality as before but does so without branching the pipeline.<\/p>\n<p>Listing 31.3 Using the Use extension method to create a health-check middleware<\/p>\n<pre><code>app.Use(async (HttpContext context, Func&lt;Task&gt; next) =&gt; \u2776\n{\nif (context.Request.Path.StartsWithSegments(&quot;\/ping&quot;)) \u2777\n{\ncontext.Response.ContentType = &quot;text\/plain&quot;; \u2778\nawait context.Response.WriteAsync(&quot;pong&quot;); \u2778\n}\nelse\n{\nawait next(); \u2779\n}\n});\napp.UseStaticFiles();<\/code><\/pre>\n<p>\u2776 The Use extension takes a lambda with HttpContext (context) and <code>Func&lt;Task&gt;<\/code> (next) parameters.<br \/>\n\u2777 The StartsWithSegments method looks for the provided segment in the current path.<br \/>\n\u2778 If the path matches, generates a response and short-circuits the pipeline<br \/>\n\u2779 If the path doesn\u2019t match, calls the next middleware in the pipeline\u2014in this case UseStaticFiles()\\<\/p>\n<p>If the incoming request starts with the required path segment (\/ping), the middleware responds and doesn\u2019t call the rest of the pipeline. If the incoming request doesn\u2019t start with \/ping, the extension calls the next middleware in the pipeline, with no branching necessary.<\/p>\n<p>With the Use extension, you have control of when and whether you call the rest of the middleware pipeline. But it\u2019s important to note that you generally shouldn\u2019t modify the Response object after calling next(). Calling next() runs the rest of the middleware pipeline, so subsequent middleware may start streaming the response to the browser. If you try to modify the response after executing the pipeline, you may end up corrupting the response or sending invalid data.<\/p>\n<p><b>WARNING<\/b> Don\u2019t modify the Response object after calling next(). Also, don\u2019t call next() if you\u2019ve written to the Response.Body; writing to this Stream can trigger Kestrel to start streaming the response to the browser, and you could cause invalid data to be sent. You can generally read from the Response object safely, such as to inspect the \ufb01nal StatusCode or ContentType of the response.<\/p>\n<p>Another common use for the Use extension method is to modify every request or response that passes through it. For example, you should send various HTTP headers with all your applications for security reasons. These headers often disable old, insecure legacy behaviors by browsers or restrict the features enabled by the browser. You learned about the HSTS header in chapter 28, but you can add other headers for additional security.<\/p>\n<p><b>TIP<\/b>  You can test the security headers for your app at <a href=\"https:\/\/securityheaders.com\">https:\/\/securityheaders.com<\/a>, which also provides information about what headers you should add to your application and why.<\/p>\n<p>Imagine you\u2019ve been tasked with adding one such header\u2014 X-Content-Type-Options: nosniff, which provides added protection against cross-site scripting (XSS) attacks\u2014 to every response generated by your app. This sort of cross- cutting concern is perfect for middleware. You can use the Use extension method to intercept every request, set the response header, and then execute the rest of the middleware pipeline. No matter what response the pipeline generates, whether it\u2019s a static \ufb01le, an error, or a Razor Page, the response will always have the security header.\u200c<\/p>\n<p>Listing 31.4 shows a robust way to achieve this. When the middleware receives a request, it registers a callback that runs before Kestrel starts sending the response back to the browser. It then calls next() to run the rest of the middleware pipeline. When the pipeline generates a response, likely in some later middleware, Kestrel executes the callback and adds the header. This approach ensures that the header isn\u2019t accidentally removed by other middleware in the pipeline and also ensures that you don\u2019t try to modify the headers after the response has started streaming to the browser.<\/p>\n<p>Listing 31.4 Adding headers to a response with the Use extension<\/p>\n<pre><code>app.Use(async (HttpContext context, Func&lt;Task&gt; next) =&gt; \u2776\n{\ncontext.Response.OnStarting(() =&gt; \u2777\n{\ncontext.Response.Headers[&quot;X-Content-Type-Options&quot;] = &quot;nosniff&quot;; \u2778\nreturn Task.CompletedTask; \u2779\n});\nawait next(); \u277a\n}\napp.UseStaticFiles(); \u277b\napp.UseRouting(); \u277b\napp.MapRazorPages \u277b<\/code><\/pre>\n<p>\u2776 Adds the middleware at the start of the pipeline<br \/>\n\u2777 Sets a function that runs before the response is sent to the browser<br \/>\n\u2778 Adds the header to the response for added protection against XSS attacks<br \/>\n\u2779 The function passed to OnStarting must return a Task.<br \/>\n\u277a Invokes the rest of the middleware pipeline<br \/>\n\u277b No matter what response is generated, it\u2019ll have the security header added.<\/p>\n<p>Simple cross-cutting middleware like the security header example is common, but it can quickly clutter your Program.cs con\ufb01guration and make it di\ufb03cult to understand the pipeline at a glance. Instead, it\u2019s common to encapsulate your middleware in a class that\u2019s functionally equivalent to the Use extension but that can be easily tested and reused.<\/p>\n<h3>31.1.4 Building a custom middleware component\u200c<\/h3>\n<p>Creating middleware with the Use extension, as you did in listings 31.3 and 31.4, is convenient, but it\u2019s not easy to test, and you\u2019re somewhat limited in what you can do. For example, you can\u2019t easily use DI to inject scoped services inside these basic middleware components. Normally, rather than call the Use extension directly, you\u2019ll encapsulate your middleware into a class that\u2019s functionally equivalent.<\/p>\n<p>Custom middleware components don\u2019t have to derive from a speci\ufb01c base class or implement an interface, but they have a certain shape, as shown in listing 31.5. ASP.NET Core uses re\ufb02ection to execute the method at runtime. Middleware classes should have a constructor that takes a RequestDelegate object, which represents the rest of the middleware pipeline, and they should have an Invoke function with a signature similar to\u200c<\/p>\n<pre><code>public Task Invoke(HttpContext context);<\/code><\/pre>\n<p>The Invoke() function is equivalent to the lambda function from the Use extension, and it is called when a request is received. The following listing shows how you could convert the headers middleware from listing 31.4 into a standalone middleware class.<\/p>\n<p>Listing 31.5 Adding headers to a Response using a custom middleware component<\/p>\n<pre><code>\npublic class HeadersMiddleware\n{\nprivate readonly RequestDelegate _next; \u2776\npublic HeadersMiddleware(RequestDelegate next) \u2776\n{ \u2776\n_next = next; \u2776\n} \u2776\npublic async Task Invoke(HttpContext context) \u2777\n{\ncontext.Response.OnStarting(() =&gt; \u2778\n{ \u2778\ncontext.Response.Headers[&quot;X-Content-Type-Options&quot;] = \u2778\n&quot;nosniff&quot;; \u2778\nreturn Task.CompletedTask; \u2778\n}); \u2778\nawait _next(context); \u2779\n}\n}<\/code><\/pre>\n<p>\u2776 The RequestDelegate represents the rest of the middleware pipeline.<br \/>\n\u2777 The Invoke method is called with HttpContext when a request is received.<br \/>\n\u2778 Adds the header to the response as before<br \/>\n\u2779 Invokes the rest of the middleware pipeline. Note that you must pass in the<br \/>\nprovided HttpContext.<\/p>\n<p><b>NOTE<\/b> Using this shape approach makes the middleware more \ufb02exible. In particular, it means you can easily use DI to inject services into the Invoke method. This wouldn\u2019t be possible if the Invoke method were an overridden base class method or an interface. However, if you prefer, you can implement the IMiddleware interface, which de\ufb01nes the basic Invoke method.<\/p>\n<p>This middleware is e\ufb00ectively identical to the example in listing 31.4, but it\u2019s encapsulated in a class called HeadersMiddleware. You can add this middleware to your app in Startup.Configure by calling<\/p>\n<pre><code>app.UseMiddleware&lt;HeadersMiddleware&gt;();<\/code><\/pre>\n<p>A common pattern is to create helper extension methods to make it easy to consume your extension method from<\/p>\n<p>Program.cs (so that IntelliSense reveals it as an option on the WebApplication instance). The following listing shows how you could create a simple extension method for HeadersMiddleware.<\/p>\n<p>Listing 31.6 Creating an extension method to expose HeadersMiddleware<\/p>\n<pre><code>public static class MiddlewareExtensions\n{\npublic static IApplicationBuilder UseSecurityHeaders( \u2776\nthis IApplicationBuilder app) \u2776\n{\nreturn app.UseMiddleware&lt;HeadersMiddleware&gt;(); \u2777\n}\n}<\/code><\/pre>\n<p>\u2776 By convention, the extension method should return an IApplicationBuilder to allow chaining.<br \/>\n\u2777 Adds the middleware to the pipeline<\/p>\n<p>With this extension method, you can now add the headers middleware to your app using<\/p>\n<pre><code>app.UseSecurityHeaders();<\/code><\/pre>\n<p><b>TIP<\/b> My SecurityHeaders NuGet package makes it easy to add security headers using middleware without having to write your own. The package provides a \ufb02uent interface for adding the recommended security headers to your app. You can \ufb01nd instructions on how to install it at <a href=\"http:\/\/mng.bz\/JggK\">http:\/\/mng.bz\/JggK<\/a>.<\/p>\n<p>Listing 31.5 is a simple example, but you can create middleware for many purposes. In some cases you may need to use DI to inject services and use them to handle a request. You can inject singleton services into the constructor of your middleware component, or you can inject services with any lifetime into the Invoke method of your middleware, as demonstrated in the following listing.<\/p>\n<p>Listing 31.7 Using DI in middleware components<\/p>\n<pre><code>public class ExampleMiddleware\n{\nprivate readonly RequestDelegate _next;\nprivate readonly ServiceA _a; \u2776\npublic HeadersMiddleware(RequestDelegate next, ServiceA a) \u2776\n{ \u2776\n_next = next; \u2776\n_a = a; \u2776\n}\npublic async Task Invoke(\nHttpContext context, ServiceB b, ServiceC c) \u2777\n{\n\/\/ use services a, b, and c\n\/\/ and\/or call _next.Invoke(context);\n}\n}<\/code><\/pre>\n<p>\u2776 You can inject additional services in the constructor. These must be singletons.<br \/>\n\u2777 You can inject services into the Invoke method. These may have any lifetime.<\/p>\n<p><b>WARNING<\/b> ASP.NET Core creates the middleware only once for the lifetime of your app, so any dependencies injected in the constructor must be singletons. If you need to use scoped or transient dependencies, inject them into the Invoke method.<\/p>\n<p>In addition to cross-cutting concerns, a good use for middleware is creating simple handlers with as few dependencies as possible that respond to a \ufb01xed URL, similar to the Use extension method you learned about in section 31.1.3. These simple handlers can be dropped into multiple applications, regardless of how the app\u2019s routing is con\ufb01gured.<\/p>\n<p>So-called well-known Uniform Resource Identi\ufb01ers (URIs) are a good use case for these simple middleware handlers, such as the security.txt well-known URI (<a href=\"https:\/\/www.rfc\">https:\/\/www.rfc<\/a>- editor.org\/rfc\/rfc9116) and the OpenID Connect URIs (<a href=\"http:\/\/mng.bz\/wvj2\">http:\/\/mng.bz\/wvj2<\/a>). These handlers always respond to a single path, so they can neatly encapsulate all the logic without risk of interfering with any other routing con\ufb01guration.\u200c<\/p>\n<p>Listing 31.8 shows a simple example of a security.txt handler implemented as middleware. It always responds to the well- known path with a \ufb01xed value and is easy to add to any application by calling app.UseMiddleware<SecurityTxtHandler>.<\/p>\n<p>Listing 31.8 A Security.txt handler implemented as middleware<\/p>\n<pre><code>public class SecurityTxtHandler\n{\nprivate readonly RequestDelegate _next;\npublic SecurityTxtHandler(RequestDelegate next)\n{\n_next = next;\n}\npublic Task Invoke(HttpContext context)\n{\nvar path = context.Request.Path;\nif(path.StartsWithSegments(&quot;\/.well-known\/security.txt&quot;)) \u2776\n{\ncontext.Response.ContentType = &quot;text\/plain&quot;; \u2777\nreturn context.Response.WriteAsync( \u2777\n&quot;Contact: mailto:security@example.com&quot;); \u2777\n}\nreturn _next.Invoke(context); \u2778\n}\n}<\/code><\/pre>\n<p>\u2776 The middleware looks for a fixed, well-known path.<br \/>\n\u2777 If the path is matched, the middleware returns a response.<br \/>\n\u2778 If the path didn\u2019t match, the next middleware in the pipeline is called.<\/p>\n<p>That covers pretty much everything you need to start building your own middleware components. By encapsulating your middleware in custom classes, you can easily test their behavior or distribute them in NuGet packages, so I strongly recommend taking this approach. Apart from anything else, it will make Program.cs \ufb01le less cluttered and easier to understand.<\/p>\n<h3>31.1.5 Converting middleware into endpoint routing endpoints\u200c<\/h3>\n<p>In this section you\u2019ll learn how you can take the custom middleware you created in section 31.1.2 and convert it to a simple middleware endpoint that integrates into the endpoint routing system. Then you can take advantage of features such as routing and authorization.<\/p>\n<p>In section 31.1.2 I described creating a simple ping-pong endpoint, using the Map and Run extension methods, that returns a plain-text pong response whenever a \/ping request is received by branching the middleware pipeline.\u200c\u200cThis is \ufb01ne because it\u2019s so simple, but what if you have more complex requirements?<\/p>\n<p>Consider a basic enhancement of this ping-pong example. How would you add authorization to the request? The AuthorizationMiddleware looks for metadata on endpoints like Razor Pages or minimal APIs to see whether there\u2019s any authorization metadata, but it doesn\u2019t know how to work with the ping-pong Map extension.<\/p>\n<p>Similarly, what if you wanted to use more complex routing? Maybe you want to be able to call \/ping\/3 and have your ping-pong middleware reply pong-pong-pong. (No, I can\u2019t think why you would either!) You now have to try to parse that integer from the URL, make sure it\u2019s valid, and so on.That\u2019s sounding like a lot more work and seems to be a clear indicator you should have created a minimal API endpoint using endpoint routing!<\/p>\n<p>For our simple ping-pong endpoint, that wouldn\u2019t be hard to do, but what if you have a more complex middleware component that you don\u2019t want to rewrite completely? Is there some way to convert the middleware to an endpoint?<\/p>\n<p>Let\u2019s imagine that you need to apply authorization to the simple ping-pong endpoint you created in section 31.1.2. This is much easier to achieve with endpoint routing than simple middleware branches like Map or Use, but let\u2019s imagine you want to stick to using middleware instead of a traditional minimal API endpoint. The \ufb01rst step is creating a standalone middleware component for the functionality,using the approach you saw in section 31.1.4, as shown in the following listing.<\/p>\n<p>Listing 31.9 The PingPongMiddleware implemented as a middleware component<\/p>\n<pre><code>public class PingPongMiddleware\n{\npublic PingPongMiddleware(RequestDelegate next) \u2776\n{\n}\npublic async Task Invoke(HttpContext context) \u2777\n{\ncontext.Response.ContentType = &quot;text\/plain&quot;; \u2778\nawait context.Response.WriteAsync(&quot;pong&quot;); \u2778\n}\n}<\/code><\/pre>\n<p>\u2776 Even though it isn\u2019t used in this case, you must inject a RequestDelegate in the<br \/>\nconstructor.<br \/>\n\u2777 Invoke is called to execute the middleware.<br \/>\n\u2778 The middleware always returns a \u201cpong\u201d response.<\/p>\n<p>Note that this middleware always returns a &quot;pong&quot; response regardless of the request URL; we will con\ufb01gure the &quot;\/ping&quot; path later. We can use this class to convert a middleware pipeline from the branching version shown in \ufb01gure 31.1, to the endpoint version shown in \ufb01gure 31.4.<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/3104.jpg\" alt=\"alt text\" \/><\/p>\n<p>Figure 31.4 Endpoint routing separates the selection of an endpoint from the execution of an endpoint. The routing middleware selects an endpoint based on the incoming request and exposes metadata about the endpoint. Middleware placed before the endpoint middleware can act based on the selected endpoint, such as short-circuiting unauthorized requests. If the request is authorized, the endpoint middleware executes the selected endpoint and generates a response.<\/p>\n<p>Converting the ping-pong middleware to an endpoint doesn\u2019t require any changes to the middleware itself. Instead, you need to create a mini middleware pipeline containing only your ping-pong middleware.<\/p>\n<p><b>TIP<\/b>  Converting response-generating middleware to an endpoint essentially requires converting it to its own mini pipeline, so you can even include additional middleware in the endpoint pipeline if you wish.<\/p>\n<p>To create the mini pipeline, you call CreateApplicationBuilder() on IEndpointRouteBuilder instance, which creates a new IApplicationBuilder. There are two ways to access the IEndpointRouteBuilder: call UseEndpoints(endpoints =&gt;{}) and use the endpoints variable or explicitly cast WebApplication to IEndpointRouteBuilder.\u200c<\/p>\n<p><b>NOTE<\/b>  Although WebApplication implements IEndpointRouteBuilder, it deliberately hides the advanced CreateApplicationBuilder() method from you! This should be a good indication that you\u2019re in advanced territory and should probably consider using minimal API endpoints instead.<\/p>\n<p>In the following listing, we create a new IApplicationBuilder, add the middleware that makes up the endpoint to it, and then call Build() to create the pipeline. Once you have a pipeline, you can associate it with a given route by calling Map() on the IEndpointRouteBuilder instance and passing in a route template.<\/p>\n<p>Listing 31.10 Mapping the ping-pong endpoint in UseEndpoints<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nWebApplication app = builder.Build();\napp.UseRouting();\napp.UseAuthentication();\napp.UseAuthorization();\nvar endpoint = ((IEndpointRouteBuilder)app) \u2776\n.CreateApplicationBuilder() \u2777\n.UseMiddleware&lt;PingPongMiddleware&gt;() \u2778\n.Build(); \u2778\napp.Map(&quot;\/ping&quot;, endpoint); \u2779\napp.MapRazorPages();\napp.MapHealthChecks(&quot;\/healthz&quot;);\napp.Run();<\/code><\/pre>\n<p>\u2776 Casts the WebApplication to IEndpointRouteBuilder so you can call<br \/>\nCreateApplicationBuilider<br \/>\n\u2777 Creates a miniature, standalone IApplicationBuilder to build your endpoint<br \/>\n\u2778 Adds the middleware and builds the final endpoint. This is executed when the<br \/>\nendpoint is executed.<br \/>\n\u2779 Maps the new endpoint with the route template \u201c\/ping\u201d<\/p>\n<p><b>TIP<\/b>  Note that the Map() function on IEndpointRouteBuilder creates a new endpoint (consisting of your mini-pipeline) with an associated route.<\/p>\n<p>Although it has the same name, this is conceptually di\ufb00erent from the Map function on IApplicationBuilder from section 31.1.2, which is used to branch the middleware pipeline. It is analogous to the MapGet (and kin) methods you use to create minimal API endpoints.<\/p>\n<p>As is common with ASP.NET Core, you can extract this somewhat-verbose functionality into an extension method to make your endpoint easier to read and discover. The following listing extracts the code to create an endpoint from listing 31.10 into a separate class, taking the route template to use as a method parameter.<\/p>\n<p>Listing 31.11 An extension method for using the PingPongMiddleware as an endpoint<\/p>\n<pre><code>public static class EndpointRouteBuilderExtensions\n{\npublic static IEndpointConventionBuilder MapPingPong( \u2776\nthis IEndpointRouteBuilder endpoints, \u2776\nstring route) \u2777\n{\nvar pipeline = endpoints\n.CreateApplicationBuilder() \u2778\n.UseMiddleware&lt;PingPongMiddleware&gt;() \u2778\n.Build(); \u2778\nreturn endpoints \u2779\n.Map(route, pipeline) \u2779\n.RequireAuthorization(); \u277a\n}\n}<\/code><\/pre>\n<p>\u2776 Creates an extension method for registering the PingPongMiddleware as an endpoint<br \/>\n\u2777 Allows the caller to pass in a route template for the endpoint<br \/>\n\u2778 Creates the endpoint pipeline<br \/>\n\u2779 Adds the new endpoint to the provided endpoint collection, using the provide route template<br \/>\n\u277a You can add additional metadata here directly, or the caller can add metadata themselves.<\/p>\n<p>Now that you have an extension method, MapPingPong(), you can update your mapping code to be simpler and easier to understand:<\/p>\n<pre><code>app.MapPingPong(&quot;\/ping&quot;); \napp.MapRazorPages(); app.MapHealthChecks(&quot;\/healthz&quot;);<\/code><\/pre>\n<p>Congratulations\u2014you\u2019ve created your \ufb01rst custom endpoint from middleware! By turning the middleware into an endpoint, you can now add extra metadata, as shown in listing 31.11. Your middleware is hooked into the endpoint routing system and bene\ufb01ts from everything it o\ufb00ers.<\/p>\n<p>The example in listing 31.11 used a basic route template, &quot;\/ping&quot;, but you can also use templates that contain route parameters, such as &quot;\/ping\/{count}&quot;, as you would with minimal APIs. The big di\ufb00erence is that you don\u2019t get the bene\ufb01ts of model binding that you get from minimal APIs, and it clearly takes more e\ufb00ort than using minimal APIs!<\/p>\n<p><b>TIP<\/b>  For examples of how to access the route data from your middleware, as well as best-practice advice, see my blog entry titled \u201cAccessing route values in endpoint middleware in ASP.NET Core 3.0\u201d at <a href=\"http:\/\/mng.bz\/4ZRj\">http:\/\/mng.bz\/4ZRj<\/a>.<\/p>\n<p>Converting existing middleware like PingPongMiddleware to work with endpoint routing can be useful when you have already implemented that middleware, but it\u2019s a lot of boilerplate to write if you want to create a new simple endpoint. In almost all cases you should use minimal API endpoints instead. But if you ever \ufb01nd yourself needing to reuse some existing middleware as an endpoint, now you know how!<\/p>\n<p>In the next section we\u2019ll move away from the middleware pipeline and look at how to handle a common con\ufb01guration requirement: using DI services to build a strongly typed IOptions objects.\u200c<\/p>\n<h2>31.2 Using DI with OptionsBuilder and ICon\ufb01gureOptions\u200c<\/h2>\n<p>In this section I describe how to handle a common scenario: you want to use services registered in DI to con\ufb01gure IOptions<T> objects. There are several ways to achieve this, but in this section I introduce the OptionsBuilder<T> as one possible approach and highlight some of the other features it enables.<\/p>\n<p>In chapter 10 we discussed the ASP.NET Core con\ufb01guration system in depth. You saw how an IConfiguration object is built from multiple layers, where subsequent layers can add to or replace con\ufb01guration values from previous layers. Each layer is added by a con\ufb01guration provider, which reads values from a \ufb01le, from environment variables, from User Secrets, or from any number of possible locations.<\/p>\n<p>A common and encouraged practice is to bind your con\ufb01guration object to strongly typed IOptions<T> objects, as you saw in chapter 10. Typically, you con\ufb01gure this binding in Program.cs by calling <code>builder.Services.Configure&lt;T&gt;()<\/code> and providing an IConfiguration object or a con\ufb01guration section to bind.<\/p>\n<p>For example, to bind a strongly typed object called CurrencyOptions to the &quot;Currencies&quot; section of an IConfiguration object, you could use the following:<\/p>\n<pre><code>builder.services.Configure&lt;CurrencyOptions&gt;( Configuration.GetSection(&quot;Currencies&quot;));<\/code><\/pre>\n<p><b>TIP<\/b>  You can see an example of the CurrencyOptions type and the associated &quot;Currencies&quot; section of appsetttings.json in the source code for this chapter.<\/p>\n<p>This sets the properties of the CurrencyOptions object, based on the values in the &quot;Currencies&quot; section of your IConfiguration object. Simple binding like this is common, but sometimes you might not want to rely on con\ufb01guring your <code>IOptions&lt;T&gt;<\/code> objects via the con\ufb01guration system; you might want to con\ufb01gure them in code instead.The IOptions pattern requires only that you con\ufb01gure a strongly typed object before it\u2019s injected into a dependent service; it doesn\u2019t mandate that you have to bind it to an IConfiguration section.<\/p>\n<p><b>TIP<\/b>  Technically, even if you don\u2019t con\ufb01gure an IOptions<T> at all, you can still inject it into your services. In that case, the T object is simply created using the default constructor.<\/p>\n<p>The Configure<T>() method has an additional overload that takes a lambda function. The framework executes the lambda function to con\ufb01gure the CurrencyOptions object when it is injected using DI. The following listing shows an example that uses a lambda function to set the Currencies property on a con\ufb01gured CurrencyOptions object to a \ufb01xed array of strings.\u200c\u200c<\/p>\n<p>Listing 31.12 Con\ufb01guring an IOptions object using a lambda function<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nbuilder.Services.Configure&lt;CurrencyOptions&gt;( \u2776\nbuilder.Configuration.GetSection(&quot;Currencies&quot;)); \u2776\nbuilder.services.Configure&lt;CurrencyOptions&gt;(options =&gt; \u2777\noptions.Currencies = new string[] { &quot;GBP&quot;, &quot;USD&quot;}); \u2777\nWebApplication app = builder.Build();\napp.MapGet(&quot;\/&quot;, (IOptions&lt;CurrencyOptions&gt; opts) =&gt; opts.Value); \u2778\napp.Run();<\/code><\/pre>\n<p>\u2776 Configures the IOptions object by binding to an IConfiguration section<br \/>\n\u2777 Configures the IOptions object by executing a lambda function<br \/>\n\u2778 The injected IOptions value is built by first binding to configuration and then applying the lambda.<\/p>\n<p>Each call to <code>Configure&lt;T&gt;()<\/code>, both the binding to IConfiguration and the lambda function, adds another con\ufb01guration step to the CurrencyOptions object. When the DI container \ufb01rst requires an instance of IOptions<CurrencyOptions>, the steps run in turn, as shown in \ufb01gure 31.5.<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/3105.jpg\" alt=\"alt text\" \/><\/p>\n<p>Figure 31.5 Con\ufb01guring a CurrencyOptions object. When the DI container needs an IOptions&lt;&gt; instance of a strongly typed object, the container creates the object and then uses each of the registered Configure() methods to set the object\u2019s properties.<\/p>\n<p>In the previous code snippet, you set the Currencies property to a static array of strings in a lambda function. But what if you don\u2019t know the correct values ahead of time? You might need to load the available currencies from a database or from some remote service, such as an ICurrencyProvider.<\/p>\n<p>This situation, in which you need a con\ufb01gured service to con\ufb01gure your <code>IOptions&lt;T&gt;<\/code>, is potentially hard to resolve. Remember that you declared your <code>IOptions&lt;T&gt;<\/code> con\ufb01guration as part of your app\u2019s DI con\ufb01guration. But if you need to resolve a service from DI to con\ufb01gure the IOptions object, you\u2019re stuck with a chicken-and-egg problem: how can you access a service from the DI container before you\u2019ve \ufb01nished con\ufb01guring the DI container?<\/p>\n<p>This circular problem has several potential solutions, but the easiest approach is to use an alternative API for con\ufb01guring IOptions instances, using the OptionsBuilder<T> type. This type is e\ufb00ectively a wrapper around some of the core IOptions interfaces, but it often results in a terser and more convenient syntax to the approach you\u2019ve seen so far.\u200c<\/p>\n<p><b>TIP<\/b>  Another helpful feature of OptionsBuilder<T> is adding validation to your IOptions objects. This ensures that your con\ufb01guration is loaded and bound correctly on app startup so that you don\u2019t have any typos in your con\ufb01guration section names, for example. You can read more about adding validation to your IOptions objects on my blog at <a href=\"http:\/\/mng.bz\/qrjJ\">http:\/\/mng.bz\/qrjJ<\/a>.<\/p>\n<p>The following listing shows the equivalent of listing 31.12 but using <code>OptionsBuilder&lt;T&gt;<\/code> instead. You create an <code>OptionsBuilder&lt;T&gt;<\/code> instance by calling <code>AddOptions&lt;T&gt; ()<\/code>, and then chain additional methods such as BindConfiguration() and Configure() to con\ufb01gure your \ufb01nal <code>IOptions&lt;T&gt;<\/code> object, building up layers of options con\ufb01guration, as shown previously in \ufb01gure 31.5.\u200c<\/p>\n<p>Listing 31.13 Con\ufb01guring an <code>IOptions&lt;T&gt;<\/code> object using <code>OptionsBuilder&lt;T&gt;<\/code><\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nbuilder.Services\n.AddOptions&lt;CurrencyOptions&gt;() \u2776\n.BindConfiguration(&quot;Currencies&quot;) \u2777\n.Configure(opts =&gt; \u2778\nopts.Currencies = new string[] { &quot;GBP&quot;, &quot;USD&quot;}); \u2778\nWebApplication app = builder.Build();\napp.MapGet(&quot;\/&quot;, (IOptions&lt;CurrencyOptions&gt; opts) =&gt; opts.Value);\napp.Run();<\/code><\/pre>\n<p>\u2776 Creates an OptionsBuilder<CurrencyOptions> object<br \/>\n\u2777 Binds to the Currencies section of the IConfiguration<br \/>\n\u2778 Configures the IOptions object by executing a lambda function<\/p>\n<p>You\u2019ve seen the builder pattern many times throughout the book, and the pattern in this case is no di\ufb00erent. The builder exposes methods that you can chain together \ufb02uently. One of the bene\ufb01ts of the builder pattern is that it\u2019s easy to discover all the methods it exposes. In this case, if you explore the type in your integrated development environment (IDE), you may notice that <code>OptionsBuilder&lt;T&gt;<\/code> exposes multiple Con\ufb01gure overloads, such as<\/p>\n<p>\u2022  Configure<TDep>(Action&lt;T,TDep&gt; config);<\/p>\n<p>\u2022  Configure&lt;TDep1,TDep2&gt;(Action&lt;T, TDep1, TDep2&gt; config);<\/p>\n<p>\u2022  Configure&lt;TDep1,TDep2,TDep3&gt; (Action&lt;T,TDep1,TDep2,TDep3&gt; config);<\/p>\n<p>These methods allow you to specify dependencies that are automatically retrieved from the DI container and passed to the con\ufb01g action when the IOptions object is fetched from DI, as shown in \ufb01gure 31.6. Five overloads for Configure<TDeps> allow you to inject dependencies, allowing you to inject up to \ufb01ve dependencies with these methods.<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/3106-1.jpg\" alt=\"alt text\" \/><br \/>\n<img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/3106-2.jpg\" alt=\"alt text\" \/><\/p>\n<p>Figure 31.6 Using OptionsBuilder to build an IOptions object. Dependencies that are requested via the Configure<TDeps> methods are automatically retrieved from the DI container and used to execute the lambda function.<\/p>\n<p>Using this pattern, we can update the code from listing 31.13 to use the ICurrencyProvider whenever our app needs to create the CurrencyOptions object. We can register the service in the DI container and know that the DI will take<\/p>\n<p>care of providing it to the lambda function at runtime, as shown in the following listing.<\/p>\n<p>Listing 31.14 Using a DI service<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nbuilder.Services\n.AddOptions&lt;CurrencyOptions&gt;()\n.BindConfiguration(&quot;Currencies&quot;)\n.Configure&lt;ICurrencyProvider&gt;((opts, service) =&gt; \u2776\nopts.Currencies = service.GetCurrencies()); \u2776\nbuilder.Services.AddSingleton&lt;ICurrencyProvider, CurrencyProvider&gt;(); \u2777\nWebApplication app = builder.Build();\napp.MapGet(&quot;\/&quot;, (IOptions&lt;CurrencyOptions&gt; opts) =&gt; opts.Value); \u2778\napp.Run();<\/code><\/pre>\n<p>\u2776 Configures the Ioptions object using a service from DI<br \/>\n\u2777 Registers the service with the DI container<br \/>\n\u2778 Retrieves the IOptions object, which retrieves the service from DI and runs the lambda method<\/p>\n<p>With the con\ufb01guration in listing 31.14, when the <code>IOptions&lt;CurrencyOptions&gt;<\/code> is \ufb01rst injected into the minimal API endpoint, the <code>IOptions&lt;CurrencyOptions&gt;<\/code> object is built as described by the OptionsBuilder. First, the &quot;Currencies&quot; section of the app IConfiguration is bound to a new CurrencyOptions object. Then the ICurrencyProvider is retrieved from DI and passed to the <code>Configure&lt;TDep&gt;<\/code> lambda, along with the options object. Finally, the IOptions object is injected into the endpoint.<\/p>\n<p><b>WARNING<\/b> You must inject only singleton services using <code>Configure&lt;TDeps&gt;<\/code> methods. If you try to inject a scoped service, such as a DbContext, you will get an error in development warning you about a captive dependency. I describe how to work around this on my blog at <a href=\"http:\/\/mng.bz\/7Dve\">http:\/\/mng.bz\/7Dve<\/a>.<\/p>\n<p>The <code>OptionsBuilder&lt;T&gt;<\/code> is a convenient way to con\ufb01gure your IOptions objects using dependencies, but you can use an alternative approach: implementing the <code>IConfigureOptions&lt;T&gt;<\/code> interface. You implement this interface in a con\ufb01guration class and use it to con\ufb01gure the IOptions<T> object in any way you need, as shown in the following listing. This class can use DI, so you can easily use any other required services.<\/p>\n<p>Listing 31.15 Implementing <code>IConfigureOptions&lt;T&gt;<\/code> to con\ufb01gure an options object<\/p>\n<pre><code>public class ConfigureCurrencyOptions : IConfigureOptions&lt;CurrencyOptions&gt;\n{\nprivate readonly ICurrencyProvider _currencyProvider; \u2776\npublic ConfigureCurrencyOptions(ICurrencyProvider currencyProvider)\n{\n_currencyProvider = currencyProvider; \u2776\n}\npublic void Configure(CurrencyOptions options) \u2777\n{\noptions.Currencies = _currencyProvider.GetCurrencies(); \u2778\n}\n}<\/code><\/pre>\n<p>\u2776 You can inject services that are available only after the DI is completely configured.<br \/>\n\u2777 Configure is called when an instance of <code>IOptions&lt;CurrencyOptions&gt;<\/code> is required.<br \/>\n\u2778 Uses the injected service to load the values<\/p>\n<p>All that remains is to register the implementation in the DI container. As always, order is important, so if you want ConfigureCurrencyOptions to run after binding to con\ufb01guration, you must add it after con\ufb01guring your <code>OptionsBuilder&lt;T&gt;<\/code>:<\/p>\n<pre><code>builder.Services.AddOptions&lt;CurrencyOptions&gt;()\n.BindConfiguration(&quot;Currencies&quot;);\nbuilder.AddSingleton\n&lt;IConfigureOptions&lt;CurrencyOptions&gt;, ConfigureCurrencyOptions&gt;();<\/code><\/pre>\n<p><b>TIP<\/b>  The order in which you con\ufb01gure your options matters. If you want to always run your con\ufb01guration last, after all other con\ufb01guration methods, you can use the PostConfigure() method on OptionsBuilder, or the IPostConfigureOptions interface. You can read more about this approach on my blog at <a href=\"http:\/\/mng.bz\/mVj4\">http:\/\/mng.bz\/mVj4<\/a>.\u200c\u200c<\/p>\n<p>With this con\ufb01guration, when IOptions<CurrencyOptions> is injected into an endpoint or service, the CurrencyOptions object is \ufb01rst bound to the &quot;Currencies&quot; section of your IConfiguration and then con\ufb01gured by the ConfigureCurrencyOptions class.\u200c<\/p>\n<p><b>WARNING<\/b> The CurrencyConfigureOptions object is registered as a singleton, so it will capture any injected services of scoped or transient lifetimes.<\/p>\n<p>Whether you use the OptionsBuilder<T> or the IConfigureOptions<T> approach, you need to register the ICurrencyProvider dependency with the DI container. In the sample code for this chapter, I created a simple CurrencyProvider service and registered it with the DI container using\u200c\u200c<\/p>\n<pre><code>builder.Services.AddSingleton&lt;ICurrencyProvider, CurrencyProvider&gt;();<\/code><\/pre>\n<p>As your app grows and you add extra features and services, you\u2019ll probably \ufb01nd yourself writing more of these simple DI registrations, where you register a Service that implements IService. The built-in ASP.NET Core DI container requires you to explicitly register each of these services manually. If you \ufb01nd this requirement frustrating, it may be time to look at third-party DI containers that can take care of some of the boilerplate for you.<\/p>\n<h2>31.3 Using a third-party dependency injection container\u200c<\/h2>\n<p>In this section I show you how to replace the default DI container with a third-party alternative, Lamar. Third-party containers often provide additional features compared with the built-in container, such as assembly scanning, automatic service registration, and property injection. Replacing the built-in container can also be useful when you\u2019re porting an existing app that uses a third-party DI container to ASP.NET Core.<\/p>\n<p>The .NET community had used DI containers for years before ASP.NET Core decided to include a built-in one. The ASP.NET Core team wanted a way to use DI in their own framework libraries, and they wanted to create a common abstraction1 that allows you to replace the built-in container with your favorite third-party alternative, such as Autofac, StructureMap\/Lamar, Ninject, Simple Injector, or Unity.<\/p>\n<p>The built-in container is intentionally limited in the features it provides, and realistically, it won\u2019t be getting many more. By contrast, third-party containers can provide a host of extra features. These are some of the features available in Lamar (<a href=\"https:\/\/jasperfx.github.io\/lamar\/guide\/ioc\">https:\/\/jasperfx.github.io\/lamar\/guide\/ioc<\/a>), the spiritual successor to StructureMap (<a href=\"https:\/\/structuremap.github.io\">https:\/\/structuremap.github.io<\/a>):<\/p>\n<p>\u2022  Assembly scanning for interface\/implementation pairs based on conventions<\/p>\n<p>\u2022  Automatic concrete class registration Property injection and constructor selection<\/p>\n<p>\u2022  Automatic Lazy<T>\/Func<T> resolution<\/p>\n<p>\u2022  Debugging\/testing tools for viewing inside your container<\/p>\n<p>None of these features is a requirement for getting an application up and running, so using the built-in container makes a lot of sense if you\u2019re building a small app or are new to DI containers in general. But if at some unde\ufb01ned tipping point, the simplicity of the built-in container becomes too much of a burden, it may be worth replacing.<\/p>\n<p><b>TIP<\/b>  A middle-of-the-road approach is to use the Scrutor NuGet package, which adds some features to the built-in DI container without replacing it. For an introduction and examples, see my blog post, \u201cUsing Scrutor to automatically register your services with the ASP.NET Core DI container\u201d at <a href=\"http:\/\/mng.bz\/MX7B\">http:\/\/mng.bz\/MX7B<\/a>.<\/p>\n<p>In this section I show how you can con\ufb01gure an ASP.NET Core app to use Lamar for dependency resolution. It won\u2019t be a complex example or an in-depth discussion of Lamar itself.Instead, I\u2019ll cover the bare minimum to get you up and running.<\/p>\n<p>Whichever third-party container you choose to install in an existing app, the overall process is pretty much the same:<\/p>\n<ol>\n<li>\n<p>Install the container NuGet package.<\/p>\n<\/li>\n<li>\n<p>Register the third-party container with WebApplicationBuilder in Program.cs.<\/p>\n<\/li>\n<li>\n<p>Con\ufb01gure the third-party container to register your services.<\/p>\n<\/li>\n<\/ol>\n<p>Most of the major .NET DI containers include adapters and extension methods to hook easily into your ASP.NET Core app. For details, it\u2019s worth consulting the speci\ufb01c guidance for the container you\u2019re using. For Lamar, the process looks like this:<\/p>\n<ol>\n<li>\n<p>Install the Lamar.Microsoft.DependencyInjection NuGet package using the NuGet package manager, by running dotnet add package<\/p>\n<pre><code>dotnet add package Lamar.Microsoft.DependencyInjection<\/code><\/pre>\n<p>or by adding a <PackageReference> to your .csproj \ufb01le:<\/p>\n<pre><code>&lt;PackageReference \nInclude=&quot;Lamar.Microsoft.DependencyInjection&quot; Version=&quot;8.1.0&quot; \/&gt;<\/code><\/pre>\n<\/li>\n<li>\n<p>Call UseLamar() on WebApplicationBuilder.Host in Program.cs:<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nbuilder.Host.UseLamar(services =&gt; {})\nWebApplication app = builder.Build();<\/code><\/pre>\n<\/li>\n<li>\n<p>Con\ufb01gure the Lamar ServiceRegistry in the lambda method passed to UseLamar(), as shown in the following listing. This is a basic con\ufb01guration, but you can see a more complex example in the source code for this chapter.<\/p>\n<\/li>\n<\/ol>\n<p>Listing 31.16 Con\ufb01guring Lamar as a third-party DI container<\/p>\n<pre><code>builder.Host.UseLamar(services =&gt; \u2776\n{\nservices.AddAuthorization(); \u2777\nservices.AddControllers() \u2777\n.AddControllersAsServices(); \u2778\nservices.Scan(_ =&gt; { \u2779\n_.AssemblyContainingType(typeof(Program)); \u2779\n_.WithDefaultConventions(); \u2779\n}); \u2779\n}<\/code><\/pre>\n<p>\u2776 Configures your services in UseLamar() instead of on builder.Services<br \/>\n\u2777 You can (and should) add ASP.NET Core framework services to the<br \/>\nServiceRegistry, as usual.<br \/>\n\u2778 Required so that Lamar is used to build your web API controllers<br \/>\n\u2779 Lamar can automatically scan your assemblies for services to register.<\/p>\n<p>In this example I\u2019ve used the default conventions to register services. This automatically registers concrete classes and services that are named following expected conventions (for example, Service implements IService). You can change these conventions or add other registrations in the UseLamar() lambda.<\/p>\n<p>The ServiceRegistry passed into UseLamar() implements IServiceCollection, which means you can use all the built-in extension methods, such as AddControllers() and AddAuthorization(), to add framework services to your container.\u200c<\/p>\n<p><b>WARNING<\/b> If you\u2019re using DI in your Model-View-Controller (MVC) controllers (almost certainly!), and you register those dependencies with Lamar rather than the built-in container, you may need to call AddControllersAsServices(), as shown in listing 31.16. This is due to an implementation detail in the way your MVC controllers are created by the framework. For details, see my blog entry titled \u201cController activation and dependency injection in ASP.NET Core MVC\u201d at <a href=\"http:\/\/mng.bz\/aogm\">http:\/\/mng.bz\/aogm<\/a>.<\/p>\n<p>With this con\ufb01guration in place, whenever your app needs to create a service, it will request it from the Lamar container, which will create the dependency tree for the class and create an instance. This example doesn\u2019t show o\ufb00 the power of Lamar, so be sure to check out the documentation (<a href=\"https:\/\/jasperfx.github.io\/lamar\">https:\/\/jasperfx.github.io\/lamar<\/a>) and the associated source code for this chapter for more examples. Even in modest-size applications, Lamar can greatly simplify your service registration code, but its party trick is showing all the services you have registered and any associated issues.<\/p>\n<p><b>TIP<\/b>  Third-party containers typically add con\ufb01guration approaches but don\u2019t change any of the fundamentals of how DI works in ASP.NET Core. All the techniques you\u2019ve seen in this book will work whether you\u2019re using the built-in container or a third-party container, so you can use the IConfigureOptions<T> approach in section 31.2, for example, regardless of which container you choose.<\/p>\n<p>That brings us to the end of this chapter on advanced con\ufb01guration. In this chapter I focused on some of the core components of any ASP.NET Core app: middleware, con\ufb01guration, and DI. In the next chapter you\u2019ll learn about more custom components, with a focus on Razor Pages and web API controllers.\u200c<\/p>\n<h2>Summary<\/h2>\n<p>Use the Run extension method to create middleware components that always return a response. You should always place the Run extension at the end of a middleware pipeline or branch, as middleware placed after it will never execute.<\/p>\n<p>You can create branches in the middleware pipeline with the Map extension. If an incoming request matches the speci\ufb01ed path pre\ufb01x, the request will execute the pipeline branch; otherwise, it will execute the trunk.<\/p>\n<p>When the Map extension matches a request path segment, it removes the segment from the request\u2019s HttpContext.Path and moves it to the PathBase property. This ensures that routing in branches works correctly.<\/p>\n<p>You can use the Use extension method to create generalized middleware components that can generate a response, modify the request, or pass the request on to subsequent middleware in the pipeline. This is useful for cross-cutting concerns, like adding a header to all responses.<\/p>\n<p>You can encapsulate middleware in a reusable class. The class should take a RequestDelegate object in the constructor and should have a public Invoke() method that takes an HttpContext and returns a Task. To call the next middleware component in the pipeline, invoke the RequestDelegate with the provided HttpContext.<\/p>\n<p>To create endpoints that generate a response, build a miniature pipeline containing the response- generating middleware, and call endpoints.Map(route, pipeline). Endpoint routing will be used to map incoming requests to your endpoint.<\/p>\n<p>You can con\ufb01gure <code>IOptions&lt;T&gt;<\/code> objects using a \ufb02uent builder interface. Call <code>AddOptions&lt;T&gt;()<\/code> to create an <code>OptionsBuilder&lt;T&gt;<\/code> instance and then chain con\ufb01guration calls.<\/p>\n<p><code>OptionsBuilder&lt;T&gt;<\/code> allows easy access to dependencies for con\ufb01guration, as well as features such as validation.<\/p>\n<p>You can also use services from the DI container to con\ufb01gure an <code>IOptions&lt;T&gt;<\/code> object by creating a separate class that implements <code>IConfigureOptions&lt;T&gt;<\/code>. This class can use DI in the constructor and is used to lazily build a requested <code>IOptions&lt;T&gt;<\/code> object at runtime.<\/p>\n<p>You can replace the built-in DI container with a third-party container. Third-party containers often provide additional features, such as convention- based dependency registration, assembly scanning, and property injection.<\/p>\n<ol>\n<li>Although the promotion of DI as a core practice has been applauded, this abstraction has seen some controversy. This post, titled \u201cWhat\u2019s wrong with the ASP.NET Core DI abstraction?\u201d, from one of the maintainers of the SimpleInjector DI library, describes many of the arguments and concerns: <a href=\"http:\/\/mng.bz\/yYAd\">http:\/\/mng.bz\/yYAd<\/a>. You can also read more about the decisions at <a href=\"http:\/\/mng.bz\/6DnA\">http:\/\/mng.bz\/6DnA<\/a>.<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>31 Advanced con\ufb01guration of ASP.NET Core\u200c This chapter covers \u2022 Building custom middleware \u2022 Using dependency injection (DI) services in IOptions con\ufb01guration \u2022 Replacing the built-in DI container with a third-party container When you\u2019re building apps with ASP.NET Core, most of your creativity and specialization go into the services and models that make up your [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-633","post","type-post","status-publish","format-standard","hentry","category-csharp"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/633","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=633"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/633\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=633"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=633"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=633"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}