How to execute some code after all actions [duplicate] - c#

I need to run some c# code each time any page based on _layout.cshtml is viewed. I don't want to put something in every controller.cs file, just something central like you used to have in ASP.NET's MasterPage.cs
Can't get this
Run a method in each request in MVC, C#?
or this
#Html.Action in Asp.Net Core
to run, not sure if it's because they're not CORE 2.0.0, I just get a lot of compilation errors. All I want to do is be able to run some code like this
public class myClass {
public static bool returnTrue() {
return true;
}
}
every time each page is loaded.

You can accomplish this with an action filter
public class GlobalFilter : IActionFilter{
public void OnActionExecuting(ActionExecutingContext context) {
//code here runs before the action method executes
}
public void OnActionExecuted(ActionExecutedContext context) {
//code here runs after the action method executes
}
}
Then in the Startup.cs file in the ConfigureServices method you wire up the ActionFilter like so:
services.AddScoped<GlobalFilter>(); //add it to IoC container.
services.AddMvc().AddMvcOptions(options => {
options.Filters.AddService(typeof(GlobalFilter)); //Tell MVC about it
});
Then you can place code in this ActionFilter which can run before every action method and you can place code in it to run after every action method. See code comments.
Through the context parameter you have access to the Controller, Controller Name, Action Descriptor, Action Name, Request object (Including the path) and so on, so there is lots of info available for you to determine which page you want to execute the code for. I'm not aware of a specific property that will tell you if the page is using _layout.cshtml but you could probably deduce that based on the other properties I mentioned.
Enjoy.

Filter would also work, but the correct way to go in .Net Core is Middleware. You can read more about it here.
If it's something simple as your example, you can go with the first examples on the link like:
app.Use(async (context, next) =>
{
returnTrue();
await next.Invoke();
});
Let me know if it helped!

Related

What is the proper way of getting the HTTP request method in an AuthorizationHandler<T>?

Simplified version of what I'm trying to do: I want to write an authorization policy that will block requests that aren't GET if a flag is set in the database. I registered my policy, the handler code runs fine for my controller, but I'm not sure what the best way of getting the HTTP method type is.
Controller looks like this:
[Authorize(Policy = "Test")]
public class MyController : ControllerBase
{
// ...
}
My handler looks like this:
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext ctx, MyRequirement req)
{
// Fetch some flag from db
if (!flag)
{
ctx.Succeed(req);
return;
}
var method = GetRequestMethodFromCtx(ctx.Resource);
if (method == HttpMethods.Get)
{
ctx.Succeed(req);
}
else
{
ctx.Fail();
}
}
I noticed that for a single request my handler gets called multiple times and that ctx.Resource is not always the same type. First time it is a RouteEndpoint, after which it is an AuthorizationFilterContextSealed.
Should I just extract the HTTP method from the RouteEndpoint and ignore the second call? Also, why are there multiple calls to the handler?
In order to access to HttpContext object you can use the IHttpContextAccessor service.
You can simply require it as a dependency of your authorization handler class and the ASP.NET core dependency injection will provide the service for you.
In order to register it with the dependency injection you need to call services.AddHttpContextAccessor() in the ConfigureServices method of your Startup class.
From the HttpContext you will have access to the Request and then to the Method property (see here).
Refer to the official documentation for the details.
I don't know exactly your requirements, but in order to prevent an action method being called with a specific HTTP verb there is a much simpler way. You just need to use the built in routing attributes, you can find more information here.

How to 'Logging' without adding a line to every method

I have a C# web api project, basically I need to log everytime a method is getting called (ie. method name, parameters passed, result, stacktrace (if error occured), etc).
Right now, I did this by adding few lines to every method and it seems bloated, is there more efficient way to achieve this?
Any help will be appreciated.
Thank you
Middleware is one approach, or you can also create a Filter that lets you perform 'before' and 'after' operations,
https://www.tutorialsteacher.com/webapi/web-api-filters
[Code from the above link]
public class LogDemoAttribute : Attribute, IActionFilter
{
public LogDemoAttribute()
{
}
public async Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
Trace.WriteLine(string.Format("Action Method {0} executing at {1}", actionContext.ActionDescriptor.ActionName, DateTime.Now.ToShortDateString()), "Web API Logs");
var result = await continuation();
Trace.WriteLine(string.Format("Action Method {0} executed at {1}", actionContext.ActionDescriptor.ActionName, DateTime.Now.ToShortDateString()), "Web API Logs");
return result;
}
public bool AllowMultiple
{
get { return true; }
}
}
This does let you add to the Global Filters (in which case, you might as well use a Middleware), or add it selectively to certain endpoints, e.g
[ApiController]
public class PeopleController : ControllerBase
{
[LogDemo]
[HttpGet("demo/endpoint")]
public IActionResult GetAll()
{
}
}
This has the advantage that you might want to pass in some parameters to the attribute, that let you perform certain context specific behaviours.
If you want to add logging to non-controller code, then you can take this approach further to an Aspect Orientated / Decorator pattern.
https://learn.microsoft.com/en-us/archive/msdn-magazine/2014/february/aspect-oriented-programming-aspect-oriented-programming-with-the-realproxy-class
I generally find logging parameters at the controller level is good enough, and something like Application Insights that generates telemetry that shows you what the request looks like is actually the useful bit of information.
When you're saying "method" I'm unsure if you mean literally every method or just whenever a controller is being hit. But middleware would probably be the way to go. The documentation on adding custom middleware is here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write?view=aspnetcore-3.1
And here is an example of error handling middleware, where you could place your logging: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-3.1

Run code when action is called in specific controller asp.net core

Say for example I have an admin controller and I want to perform additional custom checks when the user who calls any of the actions of the admin controller.
How do I execute some code within the controller class when any action is called for the controller only?
Since you are not asking for a specific programming example I will just refer you to the .net core documentation of Service Filters.
I have used these before to create something like an activity tracker for logged in users, so say it was an admin, and I wanted to see how long ago the user has performed an action, this filter would check the users last activity in a session before allowing access to the method.
Here is a generic example:
public class MyFilter : ActionFilterAttribute
{
public MyFilter()
{
}
//After Method execution
public override void OnActionExecuted(ActionExecutedContext context)
{
//Do stuff
}
//Before Method execution
public override void OnActionExecuting(ActionExecutingContext context)
{
//Do stuff
}
}
In Startup: services.AddScoped<MyFilter>();
And above controller/action: [ServiceFilter(typeof(MyFilter))]
If you want to verify user when accessing any method withing the controller, decorate your controller with [Authorize] attribute.
If you want to restrict only certain actions, you could leave your controller as is and just decorate desired methods with [Authorize] attribute.
If you wish to leave only certain methods unauthorized within controller, decorate controller with [Authorize] attribute, and the methods that you want to be accessed by anonymous users will need to be decorated using [AllowAnonymous] attribute.
Would this help? If you have specific requirements, please include them in question.

Cancel Output Caching in ChildAction

Does anyone know if it's possible to cancel output caching in code? What I mean is if I place output caching on a child action as follows can I then based on a condition cancel that caching from inside the child action?
[ChildActionOnly]
[OutputCache(Duration = 36000, VaryByParam="tagslug")]
public virtual ActionResult MostViewed(string tagslug, int count)
{
// Make an API call here. If not data returned do not cache the ChildAction as specified above
}
Skimming the framework source it looks like the only logic is don't-cache-on-exception:
// Only cache output if this wasn't an error
if (!wasException) {
ChildActionCacheInternal.Add(uniqueId, capturedText,
DateTimeOffset.UtcNow.AddSeconds(Duration));
}
I can't see a brilliant way to solve this: I think you'll have to make your own custom OutputCachingAttribute based on the original source from the ASP.NET MVC source from CodePlex, and then either add an extra check to that line for the output returned e.g.
if (!(wasException || capturedText.Contains("results:0"))) {
or similar, or find a way to pass that code a flag to this from your controller. The existing code uses an object to store a value on the session; you could copy this, e.g.
define a new static object key the same as _childActionFilterFinishCallbackKey e.g. _noCacheResultKey
add a public static method to the attribute that you can call e.g.
public static void FlagNoCache(HttpContext httpContext) {
httpContext.Items[_noCacheResultKey] = true;
}
extend ClearChildActionFilterFinishCallback to remove this from .Items[] as well as the callback
extend the above test to check this too e.g.
if (!(wasException
|| filterContext.HttpContext.Items.ContainsKey(_noCacheResultKey))) {
from your controller call MyOutputCacheAttribute.FlagNoCache(Context); as necessary.
It may also be possible to throw an exception from your code and then catch it in a different IExceptionFilter so that it doesn't get passed up beyond the OutputCacheAttribute but I don't know how sorry.

Recognize if the client is requesting page or something else in ASP.NET MVC custom HttpModule?

I have written custom HttpModule that does something every time RequestEnd event gets fired.
public void Init(HttpApplication context) {
context.EndRequest += EndEventHandler;
}
But I want it to do something only when EndRequest event is fired for the request of a html page. Not when it is a request for CSS file, picture or something else. How can I recognize what kind of content is being requested in that particular request so that I can decide what to do ?
Note: I guess similar question has been asked before but I cannot find it please add to possible duplicates if you can.
EDIT: To be more exact, I want to take some steps during the end of request if that request was processed by controller action (hmm when I think about it now maybe action filter that gets called for all actions would be better then module - is there some kind of filter that is not called when redirect action is returned?).
You can look at the content type:
if (HttpContext.Current.Response.ContentType == "text/html")
{
... you will be returning an HTML
}
or if you want to restrict only to static HTML pages you could also look at the request:
if (HttpContext.Current.Request.Url.AbsolutePath.EndsWith(".html"))
{
... it was a static html page that was requested
}
UPDATE:
Alright, I've just noticed that your question was tagged with asp.net-mvc. At first when I saw an HttpModule I thought you were doing a normal ASP.NET application (I couldn't even imagine an HttpModule in an MVC application).
Now that this has been clear you obviously could use a global action filter in which you can override the OnActionExecuting and OnActionExecuted methods which will be invoked respectively before and after a controller action is executed.
As far as your question about the redirect action in the OnActionExecuted method you could look at the filterContext.Result and see if it is a RedirectToRouteResult type:
public class MyActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
if (!(filterContext.Result is RedirectToRouteResult))
{
...
}
}
}

Categories

Resources