Web API Access Route Template inside Custom Action Filter - asp.net-web-api

I want to access RouteTemplate inside custom action filter in my Web Api Project.
I have registered a custom action filter to be executed before any action, as below.
public class AuthorizationFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext filterContext)
{
}
}
If you can see in below image I can access route template from Quick Watch inside filter. But if I write same code, it throws an error 'System.Web.Http.WebHost.Routing.HttpWebRoute' is inaccessible due to its protection level
Is there any other way to access route template
Property Using : (((System.Web.Http.WebHost.Routing.HttpWebRoute)(HttpContext.Current.Request.RequestContext.RouteData.Route)).HttpRoute).RouteTemplate

This is how it will work.
public override void OnActionExecuting(HttpActionContext filterContext)
{
if (Settings.GetKeyValue<string>("EnableAuthorization") == "Enabled")
{
var routeTemplate = filterContext.Request.GetRouteData().Route.RouteTemplate;
}
}

Related

How to unit test an action filter attribute for web api in asp.net core?

I have written an action filter for a web api. If a method in the api controller throws an unhandled exception, then the filter creates an internal error 500 response.
I need to know how to test the filter?
I have researched extensively but could not create a suitable test. I tried context mocking, a service locator implementation and even an integration test using a test server.
The web api controller looks like this:
namespace Plod.Api.ApiControllers
{
[TypeFilter(typeof(UnhandledErrorFilterAttribute))]
[Route("api/[controller]")]
public class GamesController : BaseApiController
{
public GamesController(IGameService repository,
ILogger<GamesController> logger,
IGameFactory gameFactory
) : base(
repository,
logger,
gameFactory
)
{ }
// ..... controller methods are here
}
}
The complete controller is found here.
The filter is this:
namespace Plod.Api.Filters
{
public class UnhandledErrorFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Exception != null)
{
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
filterContext.ExceptionHandled = true;
}
}
}
}
I even welcome changes to the filter implementation as a possible work around. Any help or ideas would be much appreciated. Thanks.
You probably can't. However, what you can do is spin up a TestServer and then hit it with a HttpClient. This really is an integration test and not a unit test. However, it's the good kind of integration test because it can be run safely in pipelines.
This document explains how to do this:
https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.1
The issue you are going to face is that you will need to mock the underlying services inside your app. If you don't do that, your whole server will spin up and attempt to hit the database etc. Here is an example. This is using Moq. Incidentally I am sharing the ConfigureServices method with unit tests so they use the same object mesh of mocked services. You can still use the full functionality of Moq or NSubstitute to test the back-end (or even front -end).
I can hit my attributes in the test with breakpoint.
private void ConfigureServices(IServiceCollection services)
{
var hostBuilder = new WebHostBuilder();
hostBuilder.UseStartup<TestStartup>();
hostBuilder.ConfigureServices(services =>
{
ConfigureServices(services);
});
_testServer = new TestServer(hostBuilder);
_httpClient = _testServer.CreateClient();
}
private void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(_storageManagerFactory.Object);
services.AddSingleton(_blobReferenceManagerMock.Object);
services.AddSingleton(_ipActivitiesLoggerMocker.Object);
services.AddSingleton(_loggerFactoryMock.Object);
services.AddSingleton(_hashingService);
services.AddSingleton(_settingsServiceMock.Object);
services.AddSingleton(_ipActivitiesManager.Object);
services.AddSingleton(_restClientMock.Object);
_serviceProvider = services.BuildServiceProvider();
}
public class TestStartup
{
public void Configure(
IApplicationBuilder app,
ISettingsService settingsService)
{
app.Configure(settingsService.GetSettings());
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var mvc = services.AddMvc(option => option.EnableEndpointRouting = false);
mvc.AddApplicationPart(typeof(BlobController).Assembly);
services.AddSingleton(new Mock<IHttpContextAccessor>().Object);
return services.BuildServiceProvider();
}
}

How to specify response type in ASP.NET Core middleware

My controllers return unified RequestResult:
public Task<RequestResult> SomeAction()
{
...
return new RequestResult(RequestResultType.NotFound);
}
public class RequestResult
{
public RequestResultType Type { get;set; }
... //actual data
}
public enum RequestResultType
{
Success = 1,
NotFound = 2
}
So basically RequestResult combines actual Action data and error type (if it happened). Now I need to specify Response Type at some point in case if Action returned Error. My best guess here is to use Middleware:
public class ResponseTypeMiddleware
{
private readonly RequestDelegate next;
public ResponseTypeMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
await next(context);
var response = context.Response.Body; //how to access object?
}
}
but I can't figure out what to do with it. What I'd perfectly like to do is to check if response is of type RequestResult, then specify ResponseType equal BadRequest. But I don't see how I can do it here as what I have is just a stream. May be I can hijack into pipeline earlier, before result was serialized (Controller?).
P. S. The reason why I don't use Controller.BadRequest directly in Action is that my Action's logic is implemented via CQRS command/query handlers, so I don't have direct access to Controller.
As you are going to process controller's action result (MVC), the best way is to use ActionFilter or ResultFilter here, instead of Middleware. Filters in ASP.NET Core are a part of MVC and so know about controllers, actions and so on. Middleware is a more common conception - it is an additional chain in application request-response pipeline.
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// do something before the action executes
}
public void OnActionExecuted(ActionExecutedContext context)
{
// do something after the action executes
// get or set controller action result here
var result = context.Result as RequestResult;
}
}

How to selectively enable JSONP on WebAPI actions?

I'm using ASP.NET Web API v2.0 to build an web api.
I need to make some of the controllers/actions available in CORS/JSONP, so I chose to use WebApiContrib.Formatting.Jsonp.
Because I'm not use Web API v2.1 yet, I can only use WebApiContrib v0.9.7.0.
If I add the JSONP formatter in Global.ascx.cs, it'll open all my controllers and actions for CORS/JSONP, so I wrote the Action Filter below to add and remove the formatter at specific times.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class EnableCorsAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
var config = System.Web.Http.GlobalConfiguration.Configuration;
config.Formatters.Insert(0, new WebApiContrib.Formatting.Jsonp.JsonpMediaTypeFormatter(config.Formatters.JsonFormatter));
}
public override void OnActionExecuted(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext)
{
var config = System.Web.Http.GlobalConfiguration.Configuration;
config.Formatters.RemoveAt(0);
}
}
Now, my question is that will that code be thread safe if multiple requests are coming in?

Log duration of an ASP Web API action

I am using an ActionFilter to log all action calls of my ASP.NET Web API project. The OnActionExecuted method tells a lot about what's been happening.
I just can't figure out how to find an efficient way to measure execution time...
Something like this should do the trick...
public class StopwatchAttribute : ActionFilterAttribute
{
private const string StopwatchKey = "StopwatchFilter.Value";
public override void OnActionExecuting(HttpActionContext actionContext)
{
base.OnActionExecuting(actionContext);
actionContext.Request.Properties[StopwatchKey] = Stopwatch.StartNew();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
Stopwatch stopwatch = (Stopwatch)actionExecutedContext.Request.Properties[StopwatchKey];
// TODO something useful with stopwatch.Elapsed
Trace.WriteLine("Elapsed = " + stopwatch.Elapsed);
}
}
Here we store a new Stopwatch in the request properties and stop it when the request has completed.
You will need these using statements to run the above example:
using System.Diagnostics;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
I registered the filter globally for my whole api in WebApiConfig class like this: config.Filters.Add(new StopwatchAttribute());
and in case you want to print out the name of the controller with the elapsed time, here is how you can get the name of the controller in the OnActionExectued method:
string ControllerName = actionExecutedContext.ActionContext.ActionDescriptor.ControllerDescriptor.ControllerName;

subclassing outputcache issues in mvc3

I am having some issues understanding what is happening when I create a simple subclass of OutputCacheAttribute in MVC3. Here is the code:
public class ExampleOutputCacheAttribute : OutputCacheAttribute
{
public ExampleOutputCacheAttribute()
{
// breakpoint here
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// breakpoint here
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
// breakpoint here
base.OnActionExecuted(filterContext);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
// breakpoint here
base.OnResultExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
// breakpoint here
base.OnResultExecuted(filterContext);
}
}
The first time a controller action with this attribute is requested, the constructor and all overridden methods are hit, but if I refresh the page, none of the methods or the constructor are hit. It is as if the cache is being read from outside the OutputCacheAttribute, but looking at the MVC source code for OutputCacheAttribute, I can see that in OnActionExecuting, there is code for checking for a cached page and returning the result:
filterContext.Result = new ContentResult() { Content = cachedValue };
Can anyone shed any light on what is happening?
It seems as though the OutputCache filter is more complicated than it originally appears. For page caching, it hooks in to the standard ASP.NET output caching mechanism which uses the OutputCacheModule HttpModule in IIS. Once the filter is hit once and adds the page to the cache, subsequent requests do not hit the filter in any way. The OutputCacheModule intercepts these requests and returns the cached object higher up the pipeline.
For action caching, a separate mechanism is used. This uses a static MemoryCache and the constructor and all overridden methods are hit on every request.

Resources