I know attribute based routing works on action level but can I use same at controller level for following scenario?
I have a controller with name C1Controller but I want when url contains C1 or C2 or C3 then C1Controller to invoke. How to use Route attribute to achieve this?
Got answer from a post
"The most correct way would be to create a class that inherits ActionFilterAttribute and override OnActionExecuting method. This can then be registered in the GlobalFilters in Global.asax.cs"
Like:
public class InspectActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//Check URL for c1, c2 ... and redirect if found
}
}
To use it, just add it to the global filters in global.asax:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new LogActionFilter());
}
Ref: Intercept all calls
ASP.NET MVC 4 intercept all incoming requests
Hope this helps!
Try This one
public class TheActionFilter: ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
if (controllerName !="C1" || controllerName !="C1" || controllerName !="C3")
return;
var redirectTarget = new RouteValueDictionary
{{"action", "ActionName"}, {"controller", "ControllerName"}};
filterContext.Result = new RedirectToRouteResult(redirectTarget);
filterContext = new RedirectResult(filterContext.HttpContext.Request.UrlReferrer.AbsolutePath) // The session you can get from the context like that:
var session = filterContext.HttpContext.Session;
}
}
In Your Controller
[TheActionFilter]
public class BookController : Controller
{
// Your Action Results
}
Related
Hy, I'm using Attribute Routing for my project and I don't know how I can take the value of the parameter from the URL. I tried using the Request but I can't find it anywhere.
When I make GET: http://localhost:60163/courses/courseId=1 how can I take the value of 1 for courseId?
[RoutePrefix("courses")]
public class CoursesController : ApiController
{
[Route("{courseId}")] //this is the value I need in the TeacherAuthenticationAttribute Action Filter
[TeacherAuthorizationRequiered]
public async Task<IHttpActionResult> GetCourse(int courseId=0)
{
Course course = await db.Courses.FindAsync(courseId);
if (course == null)
return NotFound();
return Ok(course);
}
In TeacherAuthorizationFilter I need the value of the courseId in order to validate it.
public class TeacherAuthorizationRequieredAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext filterContext)
{
if (filterContext.Request.Headers.Contains(Token))
var courseIdValue = filterContext.Request.RequestUri.ParseQueryString()["courseId"];
}
}
Here is the problem, I don't know how I can get the value using the Request or if there is another way to do it. Thank you very much!
public class TeacherAuthorizationRequieredAttribute : ActionFilterAttribute
{
private const string Token = "Token";
public override void OnActionExecuting(HttpActionContext filterContext)
{
if (filterContext.Request.Headers.Contains(Token))
var courseIdValue = filterContext.Request.RequestUri.ParseQueryString()["courseId"];
}
}
now , you have to added in request body
Token :"yourtoken"
How can I access some ViewBag properties across all my views? I want to have some information like current user name, etc accessible everywhere, but without having to to specifically define the properties in each ActionResult method on my project
The best and straight forward way to accomplish your requirement is to make a Custom Base Controller and inherit your Controller from this Base Controller.
public class MyBaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
ViewBag.someThing = "someThing"; //Add whatever
base.OnActionExecuting(filterContext);
}
}
Now instead of inheriting Controller class,inherit MyBaseController in your Controller as shown :-
public class MyOtherController : MyBaseController
{
public ActionResult MyOtherAction()
{
//Your Stuff
return View();
}
//Other ActionResults
}
You can achieve what you want in a number of ways, each one with their pros and cons.
1. With a Base Class
public class BaseController : Controller
{
protected override ViewResult View(IView view, object model)
{
this.ViewBag.MyProperty = "value";
return base.View(view, model);
}
}
PROS: Quite simple to implement, few lines of code, highly reusable, can be opted-out at will (see comments below).
CONS: Being forced to derive all your controllers from a base class might have some impact, especially if you have a lot of controllers already in place and/or you need to derive them from other base classes.
2. With a Module
public class ViewBagPropertyModule: Module
{
protected override void AttachToComponentRegistration(IComponentRegistry cr,
IComponentRegistration reg)
{
Type limitType = reg.Activator.LimitType;
if (typeof(Controller).IsAssignableFrom(limitType))
{
registration.Activated += (s, e) =>
{
dynamic viewBag = ((Controller)e.Instance).ViewBag;
viewBag.MyProperty= "value";
};
}
}
}
PROS: None I’m aware of.
CONS: None I’m aware of (except being a bit counterintuitive).
3. With a RegisterController Hook
builder.RegisterControllers(asm)
.OnActivated(e => {
dynamic viewBag = ((Controller)e.Instance).ViewBag;
viewBag.MyProperty = "value";
});
PROS: Fast, secure, reusable: ideal for any IoC design pattern.
CONS: Not always suited for small project and/or simple websites: if you’re not using IoC you’re often not using RegisterController at all.
4. With an ActionFilter attribute
public class MyPropertyActionFilter : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
filterContext.Controller.ViewBag.MyProperty = "value";
}
}
and then in your Global.asax.cs file:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalFilters.Filters.Add(new MyPropertyActionFilter(), 0);
}
PROS: Easily the less-obtrusive method amongst those mentioned.
CONS: None I’m aware of.
I also wrote this article on my blog explaining all the above methods.
One way: Create a custom attribute, then you can apply it globally in the FilterConfig. Then you don't have to do anything in your controllers.
public class MyCustomViewActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
dynamic ViewBag = filterContext.Controller.ViewBag;
ViewBag.Id = "123";
ViewBag.Name = "Bob";
}
}
In App_Start/FilterConfig.cs:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MyCustomViewActionFilter());
}
Another way if all you need is the User information. You can add the following to the top of your view:
#using Microsoft.AspNet.Identity
Then access your User Name using the following syntax:
#User.Identity.GetUserName()
You can also override the IPrincipal implementation and provide your own properties and methods to add more information you need to render.
UPDATE: looking at MVC 6 in Asp.Net vNext this is actually baked into the framework. http://www.asp.net/vnext/overview/aspnet-vnext/vc#inj
My current solution:
Create a base controller with all needed properties (very useful and advisable).
public abstract class BaseController : Controller {
public string MyProperty { get; set; }
}
Inherits all your controllers, from the base controller.
public class MyController : BaseController {
//you can read your property here
}
In your views, add this line just after the "#model" sentence:
#{ BaseController ctr = ViewContext.Controller as BaseController; }
Now, you can use the property in your view, without populate the ViewBag, without the need of check and cast the ViewBag values, etc.
In the view, you can use an simple inline expression:
#(ctr.MyProperty)
Or do some magic logic...
#{
if(ctr.MyProperty == "whatelse") {
//do ...
}
}
Easy, fast and comfortable.
For Net Core 5 Mvc app:
Create a ActionFilter class first:
public class GlobalSettingFilter : IActionFilter
{
private IConfiguration configuration;
//For example will get data from the configuration object
public GlobalSettingFilter(IConfiguration configuration)
{
this.configuration = configuration;
}
public void OnActionExecuting(ActionExecutingContext context)
{
//Populate the ViewData or ViewBag from your data source
(context.Controller as Controller).ViewData["helpUrl"] = configuration.GetValue<String>("helpUrl");
}
public void OnActionExecuted(ActionExecutedContext context){}
}
Then, on Startup add:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddControllersWithViews(options =>
{
options.Filters.Add(new GlobalSettingFilter(Configuration));
});
}
Just for the sake of completeness, to get the configuration object use:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
...
}
You can create a base controller that is inherited by all of your controllers, and in this controller (the base one) add:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Fill your global viewbag variables here
}
Sorry for the basic question.
From within the Global.asax, I want to get the absolute path to a controller's action, like we get by calling Response.Redirect("~/subfolder") from anywhere or by calling #Url.Content("~/controller/action") from within our views.
In my Global.asax events, I'd like to do something like this:
protected void Application_BeginRequest(object sender, EventArgs args)
{
if ( string.Compare(HttpContext.Current.Request.RawUrl, "~/foo", true) == 0 )
// do something
// I'd like the "~foo" to resolve to the virtual path relative to
// the application root
}
Here is the answer for your problem
You can simply get the controller and action name like this
protected void Application_BeginRequest(object sender, EventArgs args)
{
HttpContextBase currentContext = new HttpContextWrapper(HttpContext.Current);
UrlHelper urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
RouteData routeData = urlHelper.RouteCollection.GetRouteData(currentContext);
string action = routeData.Values["action"] as string;
string controller = routeData.Values["controller"] as string;
if (string.Compare(controller, "foo", true) == 0)
// do something
// if the controller for current request if foo
}
How to check the Session Timeout
void Session_Start(object sender, EventArgs e)
{
if (Session.IsNewSession && Session["SessionExpire"] == null)
{
//Your code
}
}
You have many options to do this. But I will not recommend to use Global.asax place to do such comparisons
Option - 1
This is also very important approach. You can use HttpModule.
Option - 2
Base Controller class
Option - 3
You can apply the Action Filter to an entire Controller class like below
namespace MvcApplication1.Controllers
{
[MyActionFilter]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
return View();
}
}
}
Whenever any of the actions exposed by the Home controller are invoked – either the Index() method or the About() method, the Action Filter class will execute first.
namespace MvcApplication1.ActionFilters
{
public class MyActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//Your code for comparison
}
}
}
If you pay attention to the above code, the OnActionExecuting will execute before executing the Action Method
Option - 4
Using this approach will execute the OnActionExecuting for Index method only.
namespace MvcApplication1.Controllers
{
public class DataController : Controller
{
[MyActionFilter]
public string Index()
{
//Your code for comparison
}
}
}
How to get the current request DataTokens
RouteData.Values["controller"] //to get the current Controller Name
RouteData.Values["action"] //to get the current action Name
It´s better to create an ActionFilterAttribute and override OnActionExecuting method like this:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.ActionDescriptor.ActionName == "Foo")
{
// do something
}
base.OnActionExecuting(filterContext);
}
Then you can apply the attribute on your BaseController, for example.
I need to do the following:
I have some Controllers ready and running, but now I want to create a BaseController.
Each of my Controllers should inherit from it like this:
public class MySecondController : BaseController
thats already running so far. Now the Problem:
I want to add a ViewBag into this base controller. This ViewBag should be accessable from every view which is called in my controllers.
How to realise this?
You can override OnActionExecuting method in the overridden method you can data to ViewBag dictionary.
public abstract class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
ViewBag.someThing = "someThing"; //Add whatever
base.OnActionExecuting(filterContext);
}
}
Updated for .net Core 2019:
using Microsoft.AspNetCore.Mvc.Filters;
public abstract class BaseController : Controller
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
ViewBag.someThing = "someThing"; //Add whatever
ViewData["someThingElse"] = "this works too";
TempData["anotherThing"] = "as does this";
base.OnActionExecuting(filterContext);
}
}
public abstract class MyControllerBase : Controller
{
protected override void OnActionExecuting(ActionExecutingContext context)
{
// do some magic
}
}
All of my controllers inherit from MyControllerBase. The problem is that now I can't unit test certain methods because the filter sets some authorisation/logic flags which influence code path.
Is there any way to manually trigger OnActionExecuting? How does the pipeline trigger these events?
EDIT: to show a little more the idea behind this design in response to comments. I basically have something like this:
public abstract class MyControllerBase : Controller
{
protected override void OnActionExecuting(ActionExecutingContext context)
{
UserProperties =
_userService
.GetUserProperties(filterContext.HttpContext.User.Identity.Name);
ViewBag.UserProperties = UserProperties;
}
public UserProperties { get; private set; }
public bool CheckSomethingAboutUser()
{
return UserProperties != null
&& UserProperties.IsAuthorisedToPerformThisAction;
}
// ... etc, other methods for querying UserProperties
}
So now anywhere in View or Controller I can get details of the current user, what is their email, what authorisation they have, which department they work for etc.
Example:
public class PurchasingController : MyControllerBase
{
public ActionResult RaisePurchaseOrder(Item item)
{
// can use UserProperties from base class to determine correct action...
if (UserProperties.CanRaiseOrders)
if (UserProperties.Department == item.AllocatedDepartment)
}
}
So this design works really nice, but as you can see testing the above action is difficult as I can't directly manipulate the UserProperties in the test set up.
I'm not sure you're suppose to override OnActionExecuting like that in MCV, normally I make an ActionFilterAttribute
public class SomeMagicAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
}
}
Then your class:
[SomeMagic]
public abstract class MyControllerBase : Controller
{
}
Then in your unit test you can just do
var magic = new SomeMagicAttribute();
var simulatedContext = new ActionExecutingContext();
magic.OnActionExecuting(simulatedContext);