Entry point of WebAPI every time it is accessed - c#

I want to identify the one point which is hit every time before a request goes to the controller in the webAPI. I need to put in a custom authentication at that point. I am already doing a customAuthorization but I want to tweak in some custom authentication even before it reaches the controller.
The application_Start method only gets triggered once and so I am not quite sure what is the one place where the control goes every time we put in a URL in the browser and hit enter.

Gloabal.asax has more methods, which can be overloaded and one of them is Application_BeginRequest
And here's more detailed lifecycle. Controller factory also might help you intercepting and tweeking requests.
protected void Application_BeginRequest(object sender, EventArgs e) //Not triggered with PUT
{
//your code
}

You can opt for ActionFilterAttribute of Web API. This is triggered for every request that comes in.
Execution pipeline:
Controller Constructor > ActionFilter's OnActionExecuting > Controller action > ActionFilter's OnActionExecuted
Simple ActionFilterAttribute implementation:
public class YourFilterName : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
// pre-processing
//Your authentication logic goes here - use actionContext
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var objectContent = actionExecutedContext.Response.Content as ObjectContent;
if (objectContent != null)
{
var type = objectContent.ObjectType; //type of the returned object
var value = objectContent.Value; //holding the returned value
}
Debug.WriteLine("OnActionExecuted Response " + actionExecutedContext.Response.StatusCode.ToString());
}
}

Related

Stop Async call in Global.aspx

i have two mvc applications in one solution.
now i need to maintain session when user redirects from one application to another.
so what my logic is,
passed GUID in URL.
get GUID in another projects Global.asax file using Init() method.
Log in another user.
i done whole code, added below.
now, i am getting call in Init() method but it also calls other methods which are passed in URl.
i.e. call becomes asynch, so because of that, user redirected to other page.
do i need to change my logic or just code?
below is my Global.asax file code.
public override void Init()
{
var userId = Guid.Parse(Request["UserGUID"].ToString());
if (userId != null && userId == Guid.Parse("1B541D9A-AC3E-466F-897B-6F9033F4533C"))
//my logic of login management
}
After more then 8 hours of R&D, i found solution that really helped me.
Actually my way is proper but the method is not right.
Application_AcquireRequestState
is the proper method for my scenario.
my code is :
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
public void Application_AcquireRequestState(object sender, EventArgs e)
{
var userId = HttpContext.Current.Request["UserGUID"];
if (userId != null)
{
Session["UserGuid"] = Guid.Parse(userId.ToString());
//My logic for session handling..
}
}
}

Response Redirect Using In MVC

I want to create connection between global.asax and my controller. We are using a database in our website. We have a code to check whether the database exists or not. If it doesn't not exist, we want to display a message like "database initializing" etc..
Database check in GLOBAL.ASAX :
public class MvcApplication : System.Web.HttpApplication
{
public static bool flag;
protected void Application_Start()
{
Database.SetInitializer<PhoneDexContext>(null);
var connString = ConfigurationManager.ConnectionStrings["DatabaseConnection"].ConnectionString;
using (PhoneDexContext db = new PhoneDexContext())
{
if (!db.Database.Exists())
{
flag = true;
db.Database.CreateIfNotExists();
}
}
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
protected void Application_BeginRequest()
{
if (flag)
{
Response.Redirect("Loading/LoadingScreen");
}
}
}
But we have a problem with using response redirect. It returns
{"Response is not available in this context."}.
We have a controller which is LoadingController and it has this code;
public class LoadingController : Controller
{
// GET: Loading
public ActionResult LoadingScreen()
{
return View();
}
}
But we can't jump to this part. How can i make connection?? Thanks
First, Application_Start does not handle any user requests. It is just perform some start up initialization. It invoked only once when app starts. To do some checks based on user's actions and properly respond you need to move these checks into Application_BeginRequest method.
Second, you also need to check if user already requesting /Loading/LoadScreen before responding with redirect to that page. Otherwise you will get an infinite redirects until database created.
public class MvcApplication : HttpApplication
{
private static bool dbInitialized;
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// We can do it asynchronously to not block other initialization code.
Task.Run((Action)CreateDataBase);
}
private static void CreateDataBase()
{
Database.SetInitializer<PhoneDexContext>(null);
using (PhoneDexContext db = new PhoneDexContext())
{
if (!db.Database.Exists())
db.Database.CreateIfNotExists();
}
dbInitialized = true;
}
protected void Application_BeginRequest()
{
if (!dbInitialized && !this.Request.Url.LocalPath.StartsWith("/Loading/LoadingScreen", StringComparison.OrdinalIgnoreCase))
{
this.Response.Redirect("/Loading/LoadingScreen");
}
}
}
You can to further and move checks into the ActionFilter as you will able to work with RouteData and check action and controller parameters instead of url. Combined with nameof that would be less error-prone to route changes, renaming, refactoring and so on.
I think this answer will give you what you want: Return different views in a controller
In your case instead of the response.redirect use return View("/Loading/LoadScreen"); ...or something simular
try this
HttpContext.Current.Response.Redirect("/Loading/LoadScreen");
beacuse Response object is not avaliable in application_start method of global.asax.
you must use HttpContext.Current for any situation.
You can also use the 'Auto-Start feature' (How to warm up an ASP.NET MVC application on IIS 7.5? : Darins answer). This will execute once, before the website is ready to serve requests. The downside is you can't show the user a 'please wait' window. Personally I would not check at every request if the database is existent or not. Just execute it; the database update should not take very long I guess?
Application_Start happens before ASP.Net starts processing the request.
You could set a global static flag to indicate the error condition, then handle Application_BeginRequest and check the flag and redirect.
static bool _isDbLoaded;
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start(){
Database.SetInitializer<PhoneDexContext>(null);
var connString = ConfigurationManager.ConnectionStrings["DatabaseConnection"].ConnectionString;
using (PhoneDexContext db = new PhoneDexContext())
{
if (!db.Database.Exists())
{
_isDbLoaded = false;
db.Database.CreateIfNotExists();
}
}
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
protected void Application_BeginRequest(){
if(!_isDbLoaded){
Response.Redirect("Loading/LoadingPage");
}
}
Since Request and Response won't be available in your Application_Start event, You might consider having this code somewhere in the MVC request-response pipeline. An action filter is a good place.
public class VerifySetupIsGood : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var dbSetDate = context.HttpContext.Application["DbSetDate"] as string;
if (String.IsNullOrEmpty(dbSetDate))
{
//to do : Execute your custom code to check db existence here
var values = new Dictionary<string, string> { { "action", "LoadScreen" },
{ "controller", "Loading" } };
var r = new RouteValueDictionary(values);
//redirect the request to MissingDatabase action method.
context.Result = new RedirectToRouteResult(r);
}
base.OnActionExecuting(context);
}
}
Here we are first checking whether the application variable has a valid entry for "DbSetDate" key. By default, it will not be there. Then you have to execute your custom code to check whether your db exist. If not, Redirect to the LoadScreen action.
Register this filter globally so that it will be executed for any request coming to your application.
GlobalFilters.Filters.Add(new VerifySetupIsGood());
Now in when you are done with setting up your database, update this application variable to have a valid value.
HttpContext.Application["DbSetDate"] = DateTime.Now.ToString();
Remember, Application variable states will also gets reset. So don't just rely on that. You should run your custom code to check your db exists inside the if condition. If condition is to prevent your custom db check for every single request.

OnAuthorization being called multiple times

I have a custom filter:
public class SetAuthFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
//do something
}
}
In the Application_Start() under the Global.asax:
GlobalFilters.Filters.Add(new SetAuthFilter());
The above codes will be called everytime an action is invoked.
However, in my _Layout.cshtml, I have 2 different "BaseController", something like:
#Html.Action("SomeAction", "Base")
#Html.Action("SomeAction", "Base2")
When I set a break point, it appears that the SetAuthFilter is always being called three times, first before the page was launched, then second time when my break point hits the BaseController, then finally the third time when my break point hits the Base2Controller.
What should I do in order to avoid SetAuthFilter being called multiple times?
You simply cannot prevent it from being called if there are multiple actions that are interacting with the filter. It will be called every single request. However, you could cache your last request for that user's identity and, if it is the same request, immediately return without continuing onto the heavier authorization checks.
public class SetAuthFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
var key = CreateKey(filterContext);
var isCached = HttpRuntime.Cache.Get(key);
if (isCached != null) return;
HttpRuntime.Cache.Insert(key, true);
// Heavy auth logic here
}
private string CreateKey(AuthorizationContext context)
{
// Or create some other unique key that allows you to identify
// the same request
return
context.RequestContext.HttpContext.User.Identity.Name + "|" +
context.RequestContext.HttpContext.Request.Url.AbsoluteUri;
}
}
Note that this doesn't account for null identities or bad URIs. You'd want to add some additional defensive checks as well as a cache invalidation strategy.
This will not allow you to bypass your authentication, since each unique request will still need to be validated. However, it minimizes the number of times you call expensive authorization logic.
For every secure controller action, the OnAuthorization overload will get called.
If you dont want that to happen, you should decorate your function with AllowAnonymous attribute.
If you don't want to call custom filter on each method:
Then remove the following line from Application_Start() under the Global.asax:
GlobalFilters.Filters.Add(new SetAuthFilter());
Add [SetAuth] attribute as follows on those methods and Controllers which really needs authorization filter :
[SetAuth]
public ActionResult Index()
{
// your code
return View(yourModel);
}

OnActionExecuting Not Getting Executed

I am troubleshooting a ASP.NET MVC application and on one server the OnActionExecuting is not firing. It has been a long time since I looked at filters. What can keep the OnActionExecuting from running? The effect in our application is the user context never really gets set up (Initialize)... so everything redirects the user back to the login page.
Here is the code of the filter. Note "Jupiter" was the codename of the project
public class JupiterAuthenticationFilter : IActionFilter
{
private readonly IJupiterContext _jupiterContext;
public JupiterAuthenticationFilter(IJupiterContext jupiterContext)
{
if (jupiterContext == null)
{
throw new ArgumentNullException("jupiterContext");
}
_jupiterContext = jupiterContext;
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
_jupiterContext.Initialize();
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
It can happen when your Controller has System.Web.MVC implementation, but ActionFilter has System.Web.Http.

ASP.NET MVC - what is the earliest point in the request cycle where you can detect a static resource request?

To give the question some context, I've written a profiler which is called on Application_BeginRequest but it's logging everything (ie javascripts, images etc). While it would be possible as a last resort to add filtering to the profiler client, I'd much rather only activate the profiler when it can be determined that the request requires routing. Ideally it would be in Application_BeginRequest but I don't think it would be possible without redundant processing of the incoming request for routing...
So in short, when is the earliest point in the request life cycle that I can determine if a request is for a static resource or not, and how would you go about it?
Is it perhaps possible to derive from or hook into System.Web.Routing.RouteTable and call my profiler code from there?
There are various options.
First - to determine the static file using Request.PhysicalPath - check out:
Check for a static file during Application_BeginRequest?
One alternative would be to have this as a handler and use the path to note the file types to include (*.aspx) for example in your web.config. Then you can have access to events pretty early on (see asp.net pipeline)
Or just use an httpmodule - check everything and only profile the non-physical items as you mention.
Or - use your current method with the first link to simply check Request.PhysicalPath and hope that works for you : )
I would rather use MVC Filters for profiling since MVC Filters allow to add pre- and post-processing behaviours and filterContext parameter should give you enough information.
For example, I would create ProfilerAttribute for profiling
public class ProfilerAttribute : FilterAttribute, IActionFilter, IResultFilter, IExceptionFilter {
public void OnActionExecuting(ActionExecutingContext filterContext) {
Debug.WriteLine("Before Action is executing");
}
public void OnActionExecuted(ActionExecutedContext filterContext) {
Debug.WriteLine("After Action is executed");
}
public void OnResultExecuted(ResultExecutedContext filterContext) {
Debug.WriteLine("After Action Result is executed");
}
public void OnResultExecuting(ResultExecutingContext filterContext) {
Debug.WriteLine("Before Action Result is executing");
}
public void OnException(ExceptionContext filterContext) {
Debug.WriteLine("oops! exception");
}
}
and register as GlobalFilter in Global.ascx....
public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
filters.Add(new HandleErrorAttribute());
filters.Add(new ProfilerAttribute());
}
Hope it can help. Thanks.
update: Forgot to mention that the MVC Filter only executed after the routing is matched. So, you don't need to filter out for static resources since it was already done by MVC.
I've tried approaching it from a different angle and am much happier with the result. Basically - why detect static resource requests when you can just not 'route' them at all? In global.asax:
private readonly string[] STATIC_CONTENT_PATHS = new string[] { "css", "js", "img" };
public static void RegisterRoutes(RouteCollection routes)
{
foreach (string path in STATIC_CONTENT_PATHS) { routes.IgnoreRoute(path + #"/{*foo}"); }
// other MapRoute calls here
}
Now I no longer need to check for static requests, although if I do want to for whatever reason I can still do the following:
protected void Application_BeginRequest()
{
if (IsStaticRequest())
{
// do something here
}
}
private bool IsStaticRequest()
{
var result = Request.Url.AbsolutePath.Split('/');
if (result.Length < 2) return false;
return STATIC_CONTENT_PATHS.Contains(result[1]);
}
Since I know with absolute certainly what the only paths I'll be serving static content from are, this seems like a fairly robust solution. I'm still open to other ideas but I think this suits my needs well.

Categories