DelegatingHandler for response in WebApi

2019-02-04 05:16发布

问题:

I am currently using several delegation handlers (classes derived from DelegatingHandler) to work on the request before it is sent, for things like validating a signature etc. This is all very nice, because I don't have to duplicate signature validation on all calls (for example).

I would like to use the same principle on the response from the same web request. Is there something similar to the DelegatingHandler for the response? A way to catch the response before it has returned to the method, in a way?

Additional information: I am calling a web api using HttpClient.PutAsync(...)

回答1:

Yes. You can do that in the continuation task.

I explain it here.

For example, this code (from the blog above) traces request URI and adds a dummy header to response.

public class DummyHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // work on the request 
       Trace.WriteLine(request.RequestUri.ToString());

       var response = await base.SendAsync(request, cancellationToken);
       response.Headers.Add("X-Dummy-Header", Guid.NewGuid().ToString());
       return response;
    }
}


回答2:

Here is an example for intercepting the request, and the response. the overridden method SendAsync is used to capture the original request, whereas the method called ResponseHandler is used to capture the response.

Example to capture original request and response

using System.Net.Http;
using System.Threading.Tasks;
namespace webAPI_Test
{
    public class MessageInterceptor : DelegatingHandler
    {
        protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            // CATCH THE REQUEST BEFORE SENDING TO THE ROUTING HANDLER
            var headers = request.ToString();
            var body = request.Content.ReadAsStringAsync().Result;
            var fullRequest = headers + "\n" + body;

            // SETUP A CALLBACK FOR CATCHING THE RESPONSE - AFTER ROUTING HANDLER, AND AFTER CONTROLLER ACTIVITY
            return base.SendAsync(request, cancellationToken).ContinueWith(
                        task =>
                        {
                            // GET THE COPY OF THE TASK, AND PASS TO A CUSTOM ROUTINE
                            ResponseHandler(task);

                            // RETURN THE ORIGINAL RESULT
                            var response = task.Result;
                            return response;
                        }
            );
        }

        public void ResponseHandler(Task<HttpResponseMessage> task)
        {
            var headers = task.Result.ToString();
            var body = task.Result.Content.ReadAsStringAsync().Result;

            var fullResponse = headers + "\n" + body;
        }
    }
}

To use this method, the class needs to be identified and registered as a MessageHandler. I added the following line to my Global.asax file...

Example how to register the new MessageInterceptor class

GlobalConfiguration.Configuration.MessageHandlers.Add(new MessageInterceptor());

Here is my complete Global.asax file. Notice how the MessageInterceptor is referenced...

Full version of Global.asax showing MessageInterceptor integration

using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace webAPI_Test
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801

    public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            GlobalConfiguration.Configuration.MessageHandlers.Add(new MessageInterceptor());
        }
    }
}