I am using .net c# MVC controller to query database for many of my projects. Every time i create a new controller, I find myself having to rewrite some of the same function for the new controller hence, I thought about writing a basic controller to handle some of the basic task that I use in all my controller (e.g., run a query and run json).
In my controller, I reference the basic controller like this.
namespace myWebAPI.Controllers
{
public class esrcController : Controller
{
//
// GET: /esrc/
string db = "esrc-";
basicController BasicController = new basicController();
public string test()
{
return "test" + Request.ServerVariables["HTTP_REFERER"];
}
public string getCodingException()
{
return #"{""data"":" + BasicController.getDataNconvertToJSON(
"select * from z_codingexception order by nc_key",
BasicController.getEnviroment(db)) + "}";
}
}
}
in my BasicController, the getEnviroment looks at the url to determine the environment hence I need access to :
Request.ServerVariables["HTTP_REFERER"] and Request.ServerVariables["HTTP_HOST"].ToString().ToLower();
but Request is null in this controller, I only have access to request in the main controller. How do I reference httpRequest from basic controller?
Just because you instantiate a new instance of a controller, doesn't mean you'll have access to the context.
One option is to create an abstract base controller that all of your other controlers would inherhit from. You'll then have access to the specific objects like Request
WebApiConfig.cs
config.MapHttpAttributeRoutes();
Your Controller
public abstract class MyBaseController : Controller
{
protected void myMethod()
{
// you have access to Request here
}
}
public class MyController : MyBaseController
{
[HttpGet]
[Route("my/getstuff")]
public IHttpActionResult GetStuff()
{
// do stuff
base.myMethod();
return Ok();
}
}
Create an action filter and add it as an attribute to that class. Within the action filter yuo wil have access to the Request object. If you override the OnActionExecuting function, the functionality in your filter will be executed before your controller.
Create a custom filter
public class CustomAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//DO STUFF WITH YOUR REQUEST OBJECT HERE..
}
}
Add the filter as an attribute to your controller
[CustomAttribute]
public class esrcController : Controller
Related
Hi guys I’m working on an Asp.net core project targeted .Net 5
I created a class inherited from IActionFilter and I used the OnActionExecution method I did some logic in it and wanna redirect from it to another Action in a Controller.
The problem :
The problem is how can I redirect to the action that request came from and I tried many solution depending on my knowledge but no one succeeds.
What I tried :
public class ValidationFilterAttribute : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
//some logic to get the model
if(!context.ModelState.IsValid)
{
Context.Result = new RedirectToAction(“Idon’t know how to get action”, “I don’t know how to get controller”,new{model= /*model I got*/});
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
Question :
I have two questions,
How to know the Action and the Controller names that request came from ?
How can I redirect to the same Action but in the same Controller and send the same model gotten in ‘OnActionExecution’
Why I do that :
My Idea is using my own ‘IActionFilter’ class with any method worked in HttpPost To check if the model sent is valid or not and if not valid On OnActionExecution method will add errors to the model and resent it again to the action.
Please any help about this issue ?
You can define a custom ActionFilter like this
public class ValidationFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; // Get controller name
var modelState = (filterContext.Controller. as Controller).ViewData.ModelState; // Get the model state of the request
var model = (filterContext.Controller as Controller).ViewData.Model; // Get the model of the request
//do your stuff here
base.OnActionExecuting(filterContext);
}
}
And then register your filter so it will be applied to every controller and every action in your project
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore(options =>
{
options.Filters.Add(typeof(ValidationFilterAttribute));
});
}
This way before hitting the controller's action method body each request will pass through your ValidationFilterAttribute where you can examine his Model and change his ModelState.
Is there a way to write a method in a class in a Asp.Net mvc application that (will be called from a controller and) redirects to a action and also supplies a parameter.
Something like.
public class someClass
{
public object Redirect(string action, string controller)
{
//some code
return RedirectToAction(action, controller new {parameter=xxx});
}
}`
Thanks
`
Try inheriting the Controller class as
public class SomeClass : Controller
{
public object Redirect(string action, string controller)
{
//some code
return RedirectToAction(action, controller new {parameter=xxx});
}
}
Though, by inheriting this class though you make your class a controller
So what I have is a base controller that the following [Route] definition
[Route("{application}/api/[controller]")]
public class BaseController
{
}
All of my current controllers inherit from BaseController.
What I am trying to achieve is that two different application can call my controllers and my code to be aware of what 'application' is calling it.
Application 1 should be able to call /Application1/Api/MyController
Application 2 should be able to call /Application2/Api/MyController
and both requests should go to the same controller but my code should be aware of which application called it.
I thought about having some sort of Middleware and then work out the application from the Request.Path, and then store it in something like HttpContext.Current.Items but that doesn't seem like the correct way to do it.
My personal preference here would be to pass the value as an HTTP header rather than a route parameter, especially if you want it everywhere. It means you don't need a Route attribute and a different URL per application. Using a custom ActionFilterAttribute, there's a bunch of ways you can pass this detail into your action. For example:
public class ApplicationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.Request.Headers.TryGetValue("Application", out var values))
{
// Method 1: This allows you to specify a parameter on your action
context.ActionArguments.Add("application", values.First());
// Method 2: This adds the value into the route data
context.RouteData.Values.Add("Application", values.First());
// Method 3: This will set a property on your controller
if (context.Controller is BaseApplicationController baseController)
{
baseController.Application = values.First();
}
}
base.OnActionExecuting(context);
}
}
And apply it to action methods or your controller:
[Application]
public class FooController : Controller
{
}
Method 1 Usage:
public IActionResult Index(string application)
{
// do something with the parameter passed in
}
Method 2 Usage:
public IActionResult Index(string application)
{
var application = (string)RouteData.Values["Application"];
}
Method 3 Usage:
First, create a base controller that contains the property:
public abstract class BaseApplicationController : Controller
{
public string Application { get; set; }
}
Then make sure your controller inherits from it:
[Application]
public class FooController : BaseApplicationController
{
}
Now you can access the property on your controller:
public IActionResult Index(string application)
{
var application = this.Application;
}
Bonus Method 4:
As an aside, you could use this method to use the URL route value, using the base controller from method 3, modify the attribute to look like this:
public class ApplicationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.Controller is BaseApplicationController baseController)
{
baseController.Application = (string)context.RouteData.Values["application"];
}
base.OnActionExecuting(context);
}
}
Add a route attribute to your controller:
[Route("{application}/api/[controller]/[action]")]
And now you should have the property value on the controller set.
You could move the route template into action and then each action would technically be aware of its caller context by your proposed convention:
[Route("api/[controller]")]
public class YourController : BaseController
{
[HttpGet("{application}")]
public IActionResult Get(string application)
{
if (application == "Application1")
{
...Application1 called
}
if (application == "Application2")
{
...Application2 called
}
...
}
}
Of course, this is your proposed convention and it is not enforced through some custom application authentication in any way so you will have to trust that your callers will correctly identify themselves through this convention.
Another approach, could be to have a base class variable and set that after inspecting the route.
[Route("{application}/api/[controller]")
public class BaseController: Controller
{
protected string CallingApp { get; set; }
public override void OnActionExecuting(ActionExecutingContext ctx)
{
CallingApp = ctx.RouteData.Values["application"];
base.OnActionExecuting(ctx);
}
}
I'm working on a WebApi project with Asp.Net.Identity v2 (with AspNetUser modified to use int IDs). I wanted to do a bit of refactoring by creating a base class for all my web api controllers. In particular, I wanted the MyBaseController class to encapsulate the logic of getting the current user id.
Previously, each action method in each controller called User.Identity.GetUserId<int>(). it was cumbersome but worked.
Now I decided to encapsulate that call either in the base class's constructor or in its property. However, it doesn't work: The Identity object I get is empty (IsAuthenticated = false, Name = null...), and GetUserId<int>() always returns 0.
Apparently it is only inside an action method that Identity is populated and GetUserId<int>() returns a correct result?
Here is basically what I get:
public class MyBaseController : ApiController
{
public int UserId => User.Identity.GetUserId<int>(); // **this always returns 0**
public MyBaseController() : base()
{ var userId = User.Identity.GetUserId<int>(); // **this is always 0** }
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
var userId = User.Identity.GetUserId<int>(); // **also 0**
}
public IHttpActionResult Get() {
var userId = User.Identity.GetuserId<int>(); // **the only place where it returns userId properly**
// some code ...
}
}
Is there a way to grab User.Identity other than in an action method to do something with it in a base class?
The project was initially created in VS 2013 with MVC 5 / WEbapi 2, now migrated to VS2015
You cannot access User.Identity before authorization. See the APIController life cycle here or on MSDN.
The information is not yet available in the constructor or in Initialize().
You can use a property in the base controller:
public abstract class MyBaseController : ApiController
{
public int UserID
{
get { return User.Identity.GetUserId<int>(); }
}
}
public class MyNormalController : MyBaseController
{
public ActionResult Index()
{
var userId = UserID; // put in variable to avoid multiple calls
// some code ...
}
}
I am reworking one asp.net MVC Backoffice for full multi tenant support.
As I decided to use sharding for a perfect separation between tenants I need to access some Auth info (tenant Id) in every viewModel, only with that info I can create the right connection for the specified tenant.
For passing that info I have 3 rules:
I don't want to use session variables
I don't want to use ViewBag
I don't want to add a new Object to every VM for get auth info
Most of all I want a "code less" solution, the perfect scenario would be getting access of auth info inside the VM for example passing it as a attribute in the controller call.
I already override Controller OnAuthorization so that it adds the tenantId to my base controller (which is the base of all my controllers) every time it is called, that way I can always catch tenantId inside every controller, now I just need a way to pass that TenantId in a attribute to every VM , something like the following pseudo-code
[Authorize]
[TenantId AS A PARAM]
public ActionResult Index()
{
myViewModel vm = new myViewModel();
vm.method();
return this.View(vm);
}
1) Put this TenantId into HttpContext.Curent.Items
2) Write a static function that returns this TenantId from the context:
private static int GetTenantId()
{
return HttpContext.Current.Items["TenantId"];
}
3) Create a BaseViewModel
public abstract class BaseViewModel
{
public Func<int> GetTenantIdFunc{get;set;}
}
4) Using Dependency Injection container register your GetTenantId function and inject it through property injection to all your models
One option is to use a base view model and inheritance:
public abstract BaseViewModel()
{
public int TenantId { get; set; }
public void SetAuthInfo(BaseController controller)
{
this.TenantId = controller.TenantId;
}
}
public MyViewModel() : BaseViewModel
// no other changes needed to MyViewModel
...
public ActionResult Index()
{
var model = new MyViewModel();
model.SetAuthInfo(this);
return View(model);
}
To get this via an attribute (rather than model.SetAuthInfo) add an action filter and override OnResultExecuted and add it there, something like (untested) :
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class SetTenantActionAttribute : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
var baseController = filterContext.Controller as BaseController;
if (baseController == null) return;
var model = filterContext.Controller.ViewData.Model as BaseViewModel;
if (model == null) return;
model.TenantId = baseController.TenantId;
}
}
then you could add this to your base controller (even less code than adding to every action)