{"id":6575,"date":"2016-09-19T15:12:47","date_gmt":"2016-09-19T22:12:47","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/dotnet\/?p=6575"},"modified":"2021-09-30T11:51:05","modified_gmt":"2021-09-30T18:51:05","slug":"custom-asp-net-core-middleware-example","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/custom-asp-net-core-middleware-example\/","title":{"rendered":"Custom ASP.NET Core Middleware Example"},"content":{"rendered":"<p>One of the great things about ASP.NET Core is its extensibility. The behavior of an ASP.NET Core app&#8217;s HTTP request handling pipeline can be easily customized by specifying different <a href=\"https:\/\/docs.asp.net\/en\/latest\/fundamentals\/middleware.html\">middleware<\/a> components. This allows developers to plug in request handlers like MVC middleware, static file providers, authentication, error pages, or even their own custom middleware. In this article, I will walk you through how to create custom middleware to handle requests with simple SOAP payloads.<\/p>\n<h3>A Disclaimer<\/h3>\n<p>Hopefully this article provides a useful demonstration of creating custom middleware for ASP.NET Core in a real-world scenario. Some users might also find the SOAP handling itself useful for processing requests from old clients that previously communicated with a basic WCF endpoint. Be aware, though, that <strong>this sample does not provide general WCF host support for ASP.NET Core<\/strong>. Among other things, it has no support for message security, WSDL generation, duplex channels, non-HTTP transports, etc. The recommended way of providing web services with ASP.NET Core is via RESTful web API solutions. The ASP.NET <a href=\"https:\/\/github.com\/aspnet\/Mvc\">MVC<\/a> framework provides a powerful and flexible model for routing and handling web requests with controllers and actions.<\/p>\n<h2>Getting Started<\/h2>\n<p>To start, create a .NET Core library (the project type is under web templates and is called <code>Class Library (package)<\/code>). Throughout this article I will be using the Preview 2 version of the <a href=\"https:\/\/www.microsoft.com\/net\/core\">.NET Core tools<\/a>.<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-22220\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2016\/09\/NewClassLibrary_thumb-1.png\" alt=\"\" width=\"600\" height=\"366\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2016\/09\/NewClassLibrary_thumb-1.png 600w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2016\/09\/NewClassLibrary_thumb-1-300x183.png 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>ASP.NET Core middleware uses the <a href=\"http:\/\/deviq.com\/explicit-dependencies-principle\/\">explicit dependencies principle<\/a>, so all dependencies should be provided through dependency injection via arguments to the middleware&#8217;s constructor. The one dependency common to most middleware is a <code>RequestDelegate<\/code> object representing the next delegate in the HTTP request processing pipeline. If our middleware does not completely handle a request, the request&#8217;s context should be passed along to this next delegate. Later, we&#8217;ll specify more dependencies in our constructor but, for now, let&#8217;s add a basic constructor to our middleware class.<\/p>\n<p>Add a dependency to <code>Microsoft.AspNetCore.Http.Abstractions<\/code> to your project.json (since that&#8217;s the contract containing <code>RequestDelegate<\/code>), give your class a descriptive name (I&#8217;m using <code>SOAPEndpointMiddleware<\/code>), and create a constructor for the middleware class like this:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=BasicMiddleware.cs\"><\/script><\/p>\n<p>Next, we need to handle incoming HTTP request contexts. For this, middleware is expected to have an <code>Invoke<\/code> method taking an <code>HttpContext<\/code> parameter. This method should take whatever actions are necessary based on the <code>HttpContext<\/code> being processed and then call the next middleware in the HTTP request processing pipeline (unless no further processing is needed). For the moment, add this trivial <code>Invoke<\/code> method:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=Invoke.cs\"><\/script><\/p>\n<p>To try out our middleware as we create it, we will need a test ASP.NET Core app. Add an ASP.NET Core web API project to your solution and set it as the startup project.<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-22221\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2016\/09\/NewWebApiProject_thumb-1.png\" alt=\"\" width=\"600\" height=\"469\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2016\/09\/NewWebApiProject_thumb-1.png 600w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2016\/09\/NewWebApiProject_thumb-1-300x235.png 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>ASP.NET Core middleware (custom or otherwise) can be added to an application&#8217;s pipeline with the <code>IApplicationBuilder.UseMiddleware&lt;T&gt;<\/code> extension method. After adding a project reference to your middleware project (<code>\"CustomMiddleware\": \"1.0.0.0\"<\/code>), add the middleware to your test app&#8217;s pipeline in the <code>Configure<\/code> method of its Startup.cs file:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=ConfigureMiddleware.cs\"><\/script><\/p>\n<p>You may notice that the other middleware components (MVC, static files, etc.) all have custom extension methods to make adding them easy. Let&#8217;s add an extension method for our custom middleware, too. I added the following method in a new source file in the custom middleware library project (notice the Microsoft.AspNetCore.Builder namespace so that IApplicationBuilder users can easily call the method):<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=MiddlewareExtension.cs\"><\/script><\/p>\n<p>The call to register the middleware in the test app then simplifies to <code>app.UseSOAPEndpoint()<\/code> instead of <code>app.UseMiddleware&lt;SOAPEndpointMiddleware&gt;()<\/code>.<\/p>\n<p>At this point, we have successfully created a basic piece of custom middleware and injected it into our test app&#8217;s HTTP request processing pipeline.<\/p>\n<p>Launch your test app (using Kestrel so that you can easily see Console output) and navigate to the hosted site with your web browser. Notice that the custom middleware logs messages as requests are received!<\/p>\n<h2>Specifying a Service Type<\/h2>\n<p>Now that we have a simple custom middleware component working, let&#8217;s have it start actually processing SOAP requests! The middleware should listen for SOAP requests to come in to a particular endpoint (URL) and then dispatch the calls to the appropriate service API based on the SOAP action specified. For this to work, our custom middleware will need a few more pieces of information:<\/p>\n<ol>\n<li>The path it should listen on for requests<\/li>\n<li>The type of the service to invoke methods from<\/li>\n<li>The <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/system.servicemodel.channels.messageencoder%28v=vs.110%29.aspx\">MessageEncoder<\/a> used to encode the incoming SOAP payloads<\/li>\n<\/ol>\n<p>These arguments will all need to be provided when an app registers our middleware as part of its processing pipeline, so let&#8217;s add them to the constructor. Note that the <code>MessageEncoder<\/code> class is in the <code>System.ServiceModel.Primitives<\/code> contract.<\/p>\n<p>After updating the constructor, it should look like this:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=SoapMiddlewareCtor.cs\"><\/script><\/p>\n<p>The <code>UseSOAPEndpoint<\/code> extension method will also need to be updated (and can be made generic to capture the service type parameter):<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=SoapMiddlewareExtension.cs\"><\/script><\/p>\n<p>Because <code>MessageEncoder<\/code> is an abstract class without any implementations publicly exposed, users of this library will have to either implement their own encoders or (more likely) extract an encoder from a WCF binding. To make that easier, let&#8217;s also add a <code>UseSOAPEndpoint<\/code> overload that takes a binding (and extracts the encoder on the user&#8217;s behalf):<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=UseSoapMiddlewareExtensionWithBinding.cs\"><\/script><\/p>\n<h2>Discovering Service Type Operations<\/h2>\n<p>Requests handled by our SOAP-handling middleware will be requests to invoke some operation on the specified service type. We can use reflection to find methods on the given service type which correspond to contract operations.<\/p>\n<p>Let&#8217;s create a new type (<code>ServiceDescription<\/code>) to store this metadata. It should take the service type as an input to its constructor and then should walk the type with reflection to discover implemented contracts and operations according to the following heuristic:<\/p>\n<ol>\n<li>Contracts should be discovered by finding <code>ServiceContractAttribute<\/code> elements on interfaces that the type implements\n<ol>\n<li>Contract name and namespace information is taken from the attribute<\/li>\n<\/ol>\n<\/li>\n<li>Operations should be discovered by finding <code>OperationContractAttribute<\/code> elements on methods within the contract interfaces\n<ol>\n<li>Operation name, properties, and action name are taken from the attribute; the method to invoke is the service type&#8217;s implementation of the interface method<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p>Note that <code>ServiceDescription<\/code>, <code>ContractDescription<\/code>, and <code>OperationDescription<\/code> used here are not the types from the <code>System.ServiceModel.Description<\/code> namespace (so you should not need to depend on that namespace). Rather, they are simple new types used for the purpose of this sample code. If the names are confusing, feel free to change them.<\/p>\n<p>The <code>ServiceDescription<\/code> type (or whatever you have named it) should end up looking something like this:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=ServiceDescription.cs\"><\/script><\/p>\n<p>The <code>ContractDescription<\/code> type is similar:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=ContractDescription.cs\"><\/script><\/p>\n<p>The <code>OperationDescription<\/code> class looks much the same except that it also contains metadata about how the operation should be invoked:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=OperationDescription.cs\"><\/script><\/p>\n<p>Once these types exist, the middleware&#8217;s constructor can be updated to store a <code>ServiceDescription<\/code> created from the specified <code>Type<\/code> instead of storing the <code>Type<\/code> itself:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=SoapMiddlewareCtor2.cs\"><\/script><\/p>\n<p>Note that this could all be simplified by just having a dictionary of action names and <code>OperationDescription<\/code> or <code>MethodInfo<\/code> dispatch methods. I&#8217;ve opted to have the whole service\/contract\/operation structure stored, though, because it will allow expanding the sample with more complex functionality (such as supporting message inspectors) in the future.<\/p>\n<h2>Invoking the Operations<\/h2>\n<p>At this point, you should have a custom middleware class that takes a service type as input and discovers available operations. Now it&#8217;s time to update the middleware&#8217;s <code>Invoke<\/code> method to actually call those operations.<\/p>\n<p>The first thing to check in the <code>Invoke<\/code> method is whether or not the incoming request&#8217;s path equals the path our service is listening on. If not, then we need to pass the request along to other pipeline members.<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=SoapMiddlewareInvoke1.cs\"><\/script><\/p>\n<p>If the request&#8217;s path <em>does<\/em> equal the expected path for our service endpoint, we need to read the message and compose a response (this code replaces the &#8216;todo&#8217; in the previous snippet).<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=SoapMiddlewareInvoke2.cs\"><\/script><\/p>\n<p>After that, we need to get the requested action by looking for a &#8216;SOAPAction&#8217; header (which is how SOAP actions are usually communicated). Again, this code replaces the &#8216;todo&#8217; from the previous snippet.<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=SoapMiddlewareInvoke3.cs\"><\/script><\/p>\n<p>Knowing the requested action, we can build on the previous snippet by finding the correct <code>OperationDescription<\/code> to invoke. Replace the previous &#8216;todo&#8217; with the following:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=SoapMiddlewareInvoke4.cs\"><\/script><\/p>\n<p>Now that we have a <code>MethodInfo<\/code> to invoke, we need to extract the arguments to pass to the operation from the request&#8217;s body. This can be done in a helper method with an <code>XmlReader<\/code> and <code>DataContractSerializer<\/code>.<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=GetRequestArguments.cs\"><\/script><\/p>\n<p>This argument reading helper assumes the arguments are provided in order in the message body. This is true for messages coming from .NET WCF clients, but may not be true for all SOAP clients. If needed, this method could be replaced with a slightly more complex variant that allows for re-ordered arguments and fuzzier parameter name matching.<\/p>\n<p>With the operation and arguments known, all that remains is to retrieve an instance of the service type to call the operation method on. This can be done with ASP.NET Core&#8217;s built-in dependency injection.<\/p>\n<p>Change the middleware&#8217;s <code>Invoke<\/code> method signature to add an <code>IServiceProvider<\/code> parameter (<code>IServiceProvider serviceProvider<\/code>). Then, we can use the <code>IServiceProver.GetService<\/code> API to retrieve service types that the user has registered in the <code>ConfigureServices<\/code> method of their Startup.cs file.<\/p>\n<p>All together, the call to invoke the operation (replacing the &#8216;todo&#8217; back in our <code>Invoke<\/code> method) should look something like this:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=SoapMiddlewareInvoke5.cs\"><\/script><\/p>\n<h2>Encoding the Response<\/h2>\n<p>Finally, with a response in hand, we can use the <code>MessageEncoder<\/code> specified by the user to send the object back to the caller in the HTTP response. <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/ms195450%28v=vs.110%29.aspx\">Message.CreateMessage<\/a> requires an implementation of <code>BodyWriter<\/code> to output the body of the message with correct element names. So, add a class like the one below that implements <code>BodyWriter<\/code>.<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=ServiceBodyWriter.cs\"><\/script><\/p>\n<p>Then we can update the middleware&#8217;s <code>Invoke<\/code> method (replacing the final &#8216;todo&#8217;) to create a response message (if the operation isn&#8217;t one way) and write it to the HTTP context&#8217;s response.<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=SoapMiddlewareInvoke6.cs\"><\/script><\/p>\n<p>And that&#8217;s it! You have written custom ASP.NET Core middleware for handling SOAP requests.<\/p>\n<h2>Testing it Out<\/h2>\n<p>Now that our custom middleware actually works with service types, the simple test app we created before will need to be updated. We&#8217;ll need a simple service type to call into. If you don&#8217;t have one on-hand to test with, you can use this sample:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=TestApp.cs\"><\/script><\/p>\n<p>The <code>UseSOAPEndpoint<\/code> call we added to the <code>Configure<\/code> method in our test host&#8217;s Startup.cs file will need updated to point to this new type: <code>app.UseSOAPEndpoint&lt;CalculatorService&gt;(\"\/CalculatorService.svc\", new BasicHttpBinding());<\/code>. Note that we&#8217;ve also created an HttpBinding (to get a message encoder from). To use <code>BasicHttpBinding<\/code>, we will need to add a reference to the <code>System.ServiceModel.Http<\/code> contract in the test app&#8217;s project.json file.<\/p>\n<p>Also, since the instance of our service is created with dependency injection, the following line will need added to the <code>ConfigureServices<\/code> method in our host&#8217;s startup.cs file: <code>services.AddSingleton&lt;CalculatorService&gt;();<\/code><\/p>\n<p>If you have a WSDL for your test service, you can create a client from that using WCF tools. Otherwise, create a client directly using <code>ClientBase&lt;T&gt;<\/code>.<\/p>\n<p>Here is a simple client I created (as a .NET Core console application) to test the middleware and host (be sure to reference <code>System.ServiceModel.Http<\/code> and <code>System.ServiceModel.Primitives<\/code> in the project.json file):<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=TestClient.cs\"><\/script><\/p>\n<p>Launch the test host and point a test client (like the one pasted above) at it to see ASP.NET Core handle a SOAP request with our custom middleware!<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-22222\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2016\/09\/Success_thumb-1.png\" alt=\"\" width=\"600\" height=\"505\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2016\/09\/Success_thumb-1.png 600w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2016\/09\/Success_thumb-1-300x253.png 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>Using a network monitoring tool like <a href=\"https:\/\/www.wireshark.org\/\">Wireshark<\/a> or <a href=\"http:\/\/www.telerik.com\/fiddler\">Fiddler<\/a>, we can observe the requests and responses.<\/p>\n<p>Request from sample:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=Request.xml\"><\/script><\/p>\n<p>Response from sample:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/mjrousos\/fc6eed8ba87fe33d86a3cc19002d6c89.js?file=Response.xml\"><\/script><\/p>\n<h2>Conclusion<\/h2>\n<p>I hope that this article has been helpful in demonstrating a real-world case of custom middleware expanding ASP.NET Core&#8217;s request processing capabilities. By creating a constructor that took the middleware&#8217;s dependencies as parameters and creating an <code>Invoke<\/code> method with the logic of deserializing and dispatching SOAP requests, we were able to serve responses to a simple WCF client from ASP.NET Core! SOAP handling middleware is just one example of how custom middleware can be used. More details on middleware are available in the <a href=\"https:\/\/docs.asp.net\/en\/latest\/fundamentals\/middleware.html\">ASP.NET Core documentation<\/a>.<\/p>\n<h2>References<\/h2>\n<ul>\n<li><a href=\"https:\/\/docs.asp.net\/en\/latest\/\">About ASP.NET Core<\/a><\/li>\n<li><a href=\"https:\/\/docs.asp.net\/en\/latest\/fundamentals\/middleware.html\">About ASP.NET Core middleware<\/a><\/li>\n<li><a href=\"http:\/\/deviq.com\/explicit-dependencies-principle\/\">Explicit dependencies principle (used by middleware types)<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>One of the great things about ASP.NET Core is its extensibility. The behavior of an ASP.NET Core app&#8217;s HTTP request handling pipeline can be easily customized by specifying different middleware components. This allows developers to plug in request handlers like MVC middleware, static file providers, authentication, error pages, or even their own custom middleware. In [&hellip;]<\/p>\n","protected":false},"author":7413,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685],"tags":[4,9,31],"class_list":["post-6575","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","tag-net","tag-net-core","tag-asp-net"],"acf":[],"blog_post_summary":"<p>One of the great things about ASP.NET Core is its extensibility. The behavior of an ASP.NET Core app&#8217;s HTTP request handling pipeline can be easily customized by specifying different middleware components. This allows developers to plug in request handlers like MVC middleware, static file providers, authentication, error pages, or even their own custom middleware. In [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/6575","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/7413"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=6575"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/6575\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=6575"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=6575"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=6575"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}