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(...)
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;
}
}
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());
}
}
}
Related
I am trying to add logging to my app using Web Api 2 and Owin, so I started using Microsoft Owin Logging, which requires an ILogger and ILoggerFactory, that has been implemented and it works great when I need to log anything inside the STARTUP method or any of the Owin Middleware components.
For example, when I am in the Startup method I can create the logger using:
public void Configuration(IAppBuilder app)
{
// Creates configuration
var configuration = new HttpConfiguration();
// Configure WebApi Settings
WebApiConfig.Register(configuration);
app.SetLoggerFactory(new OwinLog4NetLoggerFactory("Default"));
var logger = app.CreateLogger<Startup>();
logger.WriteInformation("test log");
// Enabled WebApi in OWIN
app.UseWebApi(configuration);
}
Where "OwinLog4NetLoggerFactory" is my custom ILoggerFactory implementation.
So far, so good... but... How can I create the logger when I am in the actual web api action method?... I tried accessing the Request.GetOwinEnvironment() and the logger factory is not in the dictionary.
For example:
public class AccountController : ApiController
{
public int Get(int id)
{
// Create logger here
return id + 1;
}
}
I know I can create a static class with a reference to the Logger Factory or even Injection to add the logger to the api controller, but that seems too complicated for something that should be already there.
Any ideas would be appreciated.
I'd recommend writing your middleware so that you can handle the logging outside of the controller:
public class LoggingMiddleware : OwinMiddleware
{
public LoggingMiddleware(OwinMiddleware next)
: base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
//handle request logging
await Next.Invoke(context);
//handle response logging
}
}
Then in Startup class:
public class Startup
{
// ReSharper disable once UnusedMember.Global
public void Configuration(IAppBuilder appBuilder)
{
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
appBuilder.Use<LoggingMiddleware>();
appBuilder.UseWebApi(config);
}
}
The request would then come in, hit the request logging code in the LoggingMiddleware, hit the controller code and then response would be logged on the LoggingMiddleware on the way back.
However, if all you are wanting to do is send an object through from middleware to the controller you can use context.Set("loggingObject", loggingObject); in the middleware and then
var loggingObject = Request.GetOwinContext().Get<LoggerClass>("loggingObject"); in the controller.
instead of adding logging code in every method, I create a MessageLoggingHandler that can be registered in Global.asax.cs once, and it then logs every Request and Response.
Here is the code that I use, you can modify as per your requirements:
First Create a MessageHandler class that inherits from DelegationHandler:
public abstract class MessageHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var corrId = Guid.NewGuid();
var requestMethod = request.Method.Method.ToString();
var requestUri = request.RequestUri.ToString();
var ipAddress = request.GetOwinContext().Request.RemoteIpAddress;
var requestMessage = await request.Content.ReadAsByteArrayAsync();
await LogMessageAsync(corrId, requestUri, ipAddress, "Request", requestMethod, request.Headers.ToString(), requestMessage, string.Empty);
var response = await base.SendAsync(request, cancellationToken);
var responseMessage = await response.Content.ReadAsByteArrayAsync();
await LogMessageAsync(corrId, requestUri, ipAddress, "Response", requestMethod, response.Headers.ToString(), responseMessage, ((int)response.StatusCode).ToString() + "-" + response.ReasonPhrase);
return response;
}
protected abstract Task LogMessageAsync(Guid CorrelationId, string APIUrl, string ClientIPAddress, string RequestResponse, string HttpMethod, string HttpHeaders, byte[] HttpMessage, string HttpStatusCode);
}
public class MessageLoggingHandler : MessageHandler
{
protected override async Task LogMessageAsync(Guid CorrelationId, string APIUrl, string ClientIPAddress, string RequestResponse, string HttpMethod, string HttpHeaders, byte[] HttpMessage, string HttpStatusCode)
{
// Create logger here
//Do your logging here
}
}
Then in your Global.asax.cs, you need to register the above created MessageLoggingHandler:
GlobalConfiguration.Configuration.MessageHandlers.Add(new MessageLoggingHandler());
Just be aware, this will log every request and response, will full message body. This can take a lot of space very quickly (depending on your API's usage). So you may need to tweak this (for example - keep records for a month or so, or ignore 200-OK responses etc)
I would recommend using the Common.Logging library in your applications, available on NuGet. Common.Logging gives you a common interface for using your preferred logging solution. It solves a lot of issues like yours. Here is an example using Common.Logging with NLog:
In your controller, you would access it like so:
public class MyController : ApiController
{
private static readonly ILog Log = LogManager.GetLogger<MyController>();
public async Task<IHttpActionResult> Get([FromUri] int id)
{
Log.Debug("Called Get with id " + id.ToString());
return Ok();
}
}
Pick up the latest Common.Logging.NLog package on NuGet (as of this writing, it should be Common.Logging.NLog41). Then in your web.config, you would configure Common.Logging to use your NLog configuration:
<common>
<logging>
<factoryAdapter type="Common.Logging.NLog.NLogLoggerFactoryAdapter, Common.Logging.NLog41">
<arg key="configType" value="FILE" />
<arg key="configFile" value="~/NLog.config" />
</factoryAdapter>
</logging>
</common>
Here are some additional links:
https://github.com/net-commons/common-logging
https://cmatskas.com/an-introduction-to-common-logging-api-2/
For logging purposes, I am trying to monitor the requests being made through a WebAPI. I have created and I am looking for a way to get back the body sent through in a request after the request has been fulfilled and responded to. I am trying to do this through using a ActionFilter but thus far have failed in reading the body from the request.
Can anybody give some advice how I may access this information?
For context I am trying to do this within this code:
public class LoggingActionFilter : ActionFilterAttribute
{
public override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{
var test = actionExecutedContext.Request.Content.ReadAsStringAsync().Result;
return base.OnActionExecutedAsync(actionExecutedContext, cancellationToken);
}
}
I have tried reading back the Content on the actionExecutedContext variable in order to get back the body but have found this to return just blank so far.
you're just dealing with request body so don't need to use OnActionExecutedAsync method, you can just override OnActionExecuting like this,
public override void OnActionExecuting(HttpActionContext actionContext)
{
var test = (actionContext.Request.Content as ObjectContent).Value.ToString();
// your logging code here
}
Another option available in WebAPI is DelegatingHandler. if you want to log just request body then override SendAsync method,
public class ApiLogHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
var requestBody = request.Content.ReadAsStringAsync().Result;
// your logging code here
return base.SendAsync(request, cancellationToken);
}
}
If you decided to choose DelegatingHandler then you need to register that handler to Global message handlers.
Simple question, and I am sure it has a simple answer but I can't find it.
I am using WebAPI and I would like to send back a custom header to all responses (server date/time requested by a dev for syncing purposes).
I am currently struggling to find a clear example of how, in one place (via the global.asax or another central location) I can get a custom header to appear for all responses.
Answer accepted, here is my filter (pretty much the same) and the line i added to the Register function of the WebApi config.
NOTE: The DateTime stuff is NodaTime, no real reason just was interested in looking at it.
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
actionExecutedContext.Response.Content.Headers.Add("ServerTime", Instant.FromDateTimeUtc(DateTime.Now.ToUniversalTime()).ToString());
}
Config Line:
config.Filters.Add(new ServerTimeHeaderFilter());
For that you can use a custom ActionFilter (System.Web.Http.Filters)
public class AddCustomHeaderFilter : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
actionExecutedContext.Response.Headers.Add("customHeader", "custom value date time");
}
}
You can then apply the filter to all your controller's actions by adding this in the configuration in Global.asax for example :
GlobalConfiguration.Configuration.Filters.Add(new AddCustomHeaderFilter());
You can also apply the filter attribute to the action that you want without the global cofiguration line.
Previous answers to this question don't address what to do if your controller action throws an exception. There are two basic ways to get that to work:
Add an exception filter:
using System.Net;
using System.Net.Http;
using System.Web.Http.Filters;
public class HeaderAdderExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Response == null)
context.Response = context.Request.CreateErrorResponse(
HttpStatusCode.InternalServerError, context.Exception);
context.Response.Content.Headers.Add("header", "value");
}
}
and in your WebApi setup:
configuration.Filters.Add(new HeaderAdderExceptionFilter());
This approach works because WebApi's default exception handler will send the HttpResponseMessage created in a filter instead of building its own.
Replace the default exception handler:
using System.Net;
using System.Net.Http;
using System.Web.Http.ExceptionHandling;
using System.Web.Http.Results;
public class HeaderAdderExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
HttpResponseMessage response = context.Request.CreateErrorResponse(
HttpStatusCode.InternalServerError, context.Exception);
response.Headers.Add("header", "value");
context.Result = new ResponseMessageResult(response);
}
}
and in your WebApi setup:
configuration.Services.Replace(typeof(IExceptionHandler), new HeaderAdderExceptionHandler());
You can't use both of these together. Okay, well, you can, but the handler will never do anything because the filter already converted the exception into a response.
Super important to note that as written, this code will send all the exception details to the client. You probably don't want to do this in production, so check out all the available overloads on CreateErrorResponse() and pick which one suits your needs.
Julian's answer led me to have to create the filter but only using the the System.Web (v4) and System.Web.Http (v5) namespace (MVC packages were not part of this particular project this was used on.)
using System.Web;
using System.Web.Http.Filters;
...
public class AddCustomHeaderActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
actionExecutedContext.ActionContext.Response.Headers.Add("name", "value");
}
}
And add it to the global.asax to have it used on every controller/action
GlobalConfiguration.Configuration.Filters.Add(new AddCustomHeaderActionFilterAttribute());
Neither of the above two solutions worked for me. They wouldn't even compile. Here's what I did. Added:
filters.Add(new AddCustomHeaderFilter());
to RegisterGlobalFilters(GlobalFilterCollection filters) method in FiltersConfig.cs and then added
public class AddCustomHeaderFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
{
actionExecutedContext.HttpContext.Response.Headers.Add("ServerTime", DateTime.Now.ToString());
}
}
It can be done by the messagehandler easily, it will handle both ok response and exception case.
public class CustomHeaderHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// add header to request if you want
var response = await base.SendAsync(request, cancellationToken);
response.Headers.Add("cutomKey", "cutomValue");
return response;
}
}
Add it in the config
config.MessageHandlers.Add(new CustomHeaderHandler());
According to my requirement, below single line of code serves the purpose.
System.Web.HttpContext.Current.Response.Headers.Add("Key", "Value")
I combined the normal and exception path in one class:
public class CustomHeaderAttribute : FilterAttribute, IActionFilter, IExceptionFilter
{
private static string HEADER_KEY { get { return "X-CustomHeader"; } }
private static string HEADER_VALUE { get { return "Custom header value"; } }
public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
return (new CustomHeaderAction() as IActionFilter).ExecuteActionFilterAsync(actionContext, cancellationToken, continuation);
}
public Task ExecuteExceptionFilterAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{
return (new CustomHeaderException() as IExceptionFilter).ExecuteExceptionFilterAsync(actionExecutedContext, cancellationToken);
}
private class CustomHeaderAction: ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.Response != null)
{
actionExecutedContext.Response.Content.Headers.Add(HEADER_KEY, HEADER_VALUE);
}
}
}
private class CustomHeaderException : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Response == null)
{
context.Response = context.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, context.Exception);
}
context.Response.Content.Headers.Add(HEADER_KEY, HEADER_VALUE);
}
}
}
Nothing fancy but at least it gives me one place to control my additional headers. For now it's just static content but you could always hook it up to some sort of dictionary generator/factory.
I had the same problem while trying to add a new header to the whole controller, just add "services.AddHttpContextAccessor();" to startup.cs then create your controller
public class EnController : Controller{
public EnController(IHttpContextAccessor myHttpAccessor)
{
myHttpAccessor.HttpContext.Response.Headers.Add("Content-Language", "en-US");
}
... more methods here...
}
I'm trying to log the HTTP Response Headers of my Web API project.
The project is developed by VS2012, .NET 4.5 and ASP.NET MVC 4.
I've wrote a DelegatingHandler subclass like this:
public class LoggingHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Execute the request
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
var response = task.Result;
return response;
});
}
}
However, the problem is, I can't get the header values from the response. response.Headers is an empty collection, response.Content.Headers contains nothing but a key named Content-Type, and HttpContext.Current is null.
I've seen the code of WebAPIContrib which use the same logic to log the headers, but their code does not seem to work either.
So how should I trace the HTTP Response Headers in Web API project?
Message handlers are called in the same order that they appear in
MessageHandlers collection. Because they are nested, the response
message travels in the other direction. That is, the last handler is
the first to get the response message.
Make sure that the logging handler is registered early in the pipeline. Preferably first.
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
config.MessageHandlers.Add(new LoggingHandler(...));
//...add other handlers
config.MessageHandlers.Add(new MessageHandler1());
config.MessageHandlers.Add(new MessageHandler2());
// Other code not shown...
}
}
That way any other handlers would have their chance to populate the response and have that info logged.
You can also simplify the class using async/await syntax to make accessing the response cleaner.
public class LoggingHandler : DelegatingHandler {
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
//...Extract and log request
LogRequest(request);
// Send the request on to the inner handler(s) and get the response
var response = await base.SendAsync(request, cancellationToken);
//...Extract details from response for logging
LogResponse(response);
return response;
}
private void LogRequest(HttpRequestMessage request) {
//... code removed for brevity
}
private void LogResponse(HttpResponseMessage response) {
//... code removed for brevity
}
}
Should be able to access the necessary details from the response before returning it.
Reference : HTTP Message Handlers in ASP.NET Web API
Try out the below code.
return base.SendAsync(request, cancellationToken).ContinueWith(
task =>
{
var headers = task.Result.ToString();
var body = task.Result.Content.ReadAsStringAsync().Result;
// RETURN THE ORIGINAL RESULT
var response = task.Result;
return response;
}
);
You must look at the right place:
request.Content.Headers.ContentType
Assuming there's a Content-Type: application/json request header, then the aforementioned will return "application/json".
So basically some headers are associated with the Content and that's where you should be reading them from.
Same is true for the response headers. Depending on their type you might need to extract them from the response content (for requests that return body)
response.Content.Headers.ContentType
I have to write unit test on ASP.NET MVC Web API Controller with Rhino.Mock
I have a handler named AHandler.cs with inherts from System.Net.Http.HttpClientHandler class.
The singnature SendAsync method of AHandler.cs is like followings :
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
.....
var response = base.SendAsync(request, cancellationToken).Result;
if (response.IsSuccessStatusCode)
{
.....
}
}
the base keyword above means HttpClientHandler and its SendAsync() method is "protected"!!!
Now I try to mock the object "base.SendAsync(request, cancellationToken).Result" and got the hand-made response result I wanted.
But it seems that Rhino mocks can't see the "base" keyword when I wrote the followings code :
var mockbase = MockRepository.GenerateMock<AHandler>;
mockbase.Stub(x => x.base <=== can't see base keyword
^^^^^
So I change another way and try to mock the HttpClientHandler class
var mockbase = MockRepository.GenerateMockHttpClientHandler>;
mockbase.Stub(x => x. <== I can't see SendAsync() method, becase it is protected !!
Now I really suffer in it !!
Can anybody give me some advice that how to made a custom response in MVC handler ?!
very thanks !!
Why do you want to mock a handler in first place ? You can inject an specific dummy implementation for your tests. That handler will return a new HttpResponse message expected by your tests.
public class MyDummyHttpHandler : DelegatingHandler
{
HttpResponseMessage response;
public MyDummyHttpHandler(HttpResponseMessage response)
{
this.response = response;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
.....
TaskCompletionSource<HttpResponseMessage> tsc = new TaskCompletionSource<HttpResponseMessage>();
tsc.SetResult(this.response);
return tsc.Task;
}
}