I had my NHibernate session management setup like follows:
protected MvcApplication()
{
BeginRequest += delegate
{
NHibernateSessionManager.Instance.OpenSession();
};
EndRequest += delegate
{
NHibernateSessionManager.Instance.CloseSession();
};
}
And for when I needed to save to the database, I made an ActionFilterAttribute that looked like this:
public class TransactionAttribute: ActionFilterAttribute
{
private ITransaction _currentTransaction;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_currentTransaction = NHibernateSessionManager.Instance.CurrentSession.Transaction;
_currentTransaction.Begin();
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (_currentTransaction.IsActive)
{
if (filterContext.Exception == null)
_currentTransaction.Commit();
else
{
_currentTransaction.Rollback();
}
}
_currentTransaction.Dispose();
}
}
and then I could just add [Transaction] to my action method. This seemed to work in initial testing, but I then I tried using at HttpWebRequest to call an action method from another app multiple times and I had issues. Testing with Fiddler I setup a POST request and then fired them off in quick succession and it showed up the following:
THe red ones are various errors that I believe is to do with threading.
My NHibernateSessionManager uses the HTtpContext to store the session like this:
public ISession CurrentSession
{
get { return (ISession)HttpContext.Current.Items["current.session"]; }
set { HttpContext.Current.Items["current.session"] = value; }
}
So, to fixed it, I moved my Transaction code into my BeginRequest and EndRequest methods - and then I could fire off heaps in succession.
My question is - why did this fix it? I would have thought that I would have had something similar to this:
Begin Request - opens session
OnActionExecuting - starts transaction
action code
OnActionExecuted - commits transaction
End Request - closes session
and that this would be unique to each request, so it shouldn't interfere with one another, because there should be a different HttpContext for each request shouldn't there? Or are they shared or something??
Can someone enlighten me?
Quote from the release notes of ASP.NET MVC 3:
In previous versions of ASP.NET MVC,
action filters were created per
request except in a few cases. This
behavior was never a guaranteed
behavior but merely an implementation
detail and the contract for filters
was to consider them stateless. In
ASP.NET MVC 3, filters are cached more
aggressively. Therefore, any custom
action filters which improperly store
instance state might be broken.
This basically means that the _currentTransaction instance you have in your action filter might not be what you think it is. So be careful how/when is this property injected => it is not clear from the code you have shown.
Related
I am building a library that is supposed to add features into an aspnetcore WebApp.
My goal is to add Controllers written in the library to respond to requests on specific routes on the WebApp itself.
The first thing I've done is to write an extension method as such:
public static IApplicationBuilder UseCustomLibrary(this IApplicationBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
//Here I want to hookup the routes to respond using my controller
}
Given that piece of code, I can now run app.UseCustomLibrary() from Program.cs.
Now, hooking up the actual controller to respond turns out to be not an easy task.
Things I've tried:
Using Custom Middleware:
public class MyCustomMiddleware
{
private readonly RequestDelegate _next;
public MyCustomMiddleware(RequestDelegate next)
{
_next = next ?? throw new ArgumentNullException(nameof(next));
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Path.Value.Contains("expectedpath"))
{
//Manually handle the request.
}
await _next(context);
}
}
Even though that kind of works, that doesn't really hookup the controller. Instead I can handle the request manually. I dislike that because I now have to rewrite the controller into some kind of handler. That forces to abandon the nice controller structure of automatically determining the route using area/controller/action pattern. So this is currently my plan B.
Using Custom IApplictionFeatureProviders:
internal class MyCustomFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
{
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
{
if (!feature.Controllers.Contains(typeof(CustomController).GetTypeInfo()))
{
feature.Controllers.Add(typeof(CustomController).GetTypeInfo());
}
}
}
Then I would slightly modify the extension method to be call that provider as such:
public static IApplicationBuilder UseCustomLibrary(this IApplicationBuilder app)
{
app.Services.AddMvc().PartManager.FeatureProviders.Add(new MyCustomFeatureProvider());
return builder;
}
I was trying to copy what Microsoft.Identity library does in how it adds the "AccountController". Unfortunately that also doesn't work. The controller actions are never called for some reason.
I have also tried using that in conjunction with the middleware, by replacing the invoke method of the middleware with the following:
public async Task Invoke(HttpContext context)
{
context.Features.Set<IApplicationFeatureProvider<ControllerFeature>>(new MyCustomFeatureProvider());
await _next(context);
}
Again, that simply does nothing and the controller actions are never called.
I've also tried calling app.MapControllerRoute from my extension method, but it doesn't work as well for a reason that I cannot understand. It seems the controller is not even registered so it is never called.
I am willing to provide more details. I know there must be a way to do that, since it is used all the time in Microsoft.Identity and Microsoft.AspNetCore.Authentication to respond to authentication callbacks from IdPs for example. So there must be a way to do what I want.
I need to do some authentication for a web app with MVC3. The customer would like there to be a generic page to show if they do not have any of the role groups in windows AD that are allowed to use the app. I found a pretty simple way to do it, but just curious if it is a valid way or if there is something better out there.
Basically in the Session_Start in the global I am checking for User.IsInRole() and if that returns false then I do a Response.Redirect(). This question is: after it his the code in the IF statement and hits the Response.Redirect() code then it hits the session one more time before it goes to the AccessDenied page in the root of the app. Is this okay? Will it cause any issues If they are valid and does not enter the If to do the response.redirect?
//if (!User.IsInRole("test_user"))
//{
// Response.Redirect("~/AccessDenied.aspx", true);
//}
I would recommend you to write your Authorization filter for MVC3 and do this type of logic there:
public class RoleFilter: AuthorizeAttribute
{
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext filterContext)
{
if (!User.IsInRole("test_user"))
{
filterContext.HttpContext.Response.StatusCode = 302;
filterContext.Result = new RedirectResult("~/AcessDenied.aspx");
}
}
}
Also I wouldn't recommend you to use Response.Redirect because it aborts current thread.
Where can I plug in a custom provider to set up the request context?
I want to run an ASP.NET MVC application in "slave" mode while gradually transitioning features from a legacy system. Each request will have a cookie, and I want to grab the cookie, make an external call to resolve it to a user identity, and set up that user identity for the remainder of the request.
I might set a forms authentication cookie, or use Session, but the source of truth about authentication has to be the externally-set cookie, on every request.
What's the best way to do this? Where do I plug in? I've looked at Authentication providers, and the Authorization attribute, but neither of those seems the right place for this.
I would have thought an HttpModule would be ideal for this scenario?
If I understand you correctly, I did something similar on a project I was working on recently:
public class UserSessionHttpModule : IHttpModule
{
private HttpApplication mApplication;
public void Dispose()
{
}
public void Init(HttpApplication context)
{
mApplication = context;
context.BeginRequest += new EventHandler(CheckUserSession);
}
private void CheckUserSession(Object sender, EventArgs e)
{
var extension = Path.GetExtension(mApplication.Context.Request.Path);
if (extension == "" || extension == ".aspx")
{
var userSessionService = ObjectFactory.GetInstance<IUserSessionService>();
userSessionService.CheckUserSession();
}
}
}
you could override the Init method in Global.asax page and listen to the PostAuthenticateRequest event, the events fires after authentication so you could change whatever values you like from the form authenticate cookies and inject your own
First of all, sorry for my bad english.
I have faced a strange problem using asp net mvc.
I have simple controller, which can execute 2 operations. The first operation is continuous and can take a several minutes. And the other is short, and executed some seconds.
Something like this:
public class TestController : Controler {
[HttpPost]
public string Func1(long id) {
// continuous operation
return new ValueGetter().Get(id)
}
[HttpPost]
public string Func2(long id) {
return "Abc";
}
}
And from the client side i call thouse methods via jqueries post:
$.post(url).sucess(...);
The problem consists in the next: while the first operation is executed, the second operation will wait, until first is finished.
I tried to use AsyncController as described there http://msdn.microsoft.com/en-us/library/ee728598.aspx, but the result is the same...
I have logged some application events in global.asax:
protected void Application_PostMapRequestHandler(object sender, EventArgs e) {
LoggerManager.Info("PostMapRequestHandler fired in global.asax");
}
protected void Application_AcquireRequestState(object sender, EventArgs e) {
LoggerManager.Info("AcquireRequestState fired in global.asax");
}
And if I for example, call the first method once, and then Immediately call the second method three times, I have the following result in the log file:
1.PostMapRequestHandler
2.AcquireRequestState
3.PostMapRequestHandler
4.PostMapRequestHandler
5.PostMapRequestHandler
... after first method is executed sucessfuly
6.AcquireRequestState
7.AcquireRequestState
8.AcquireRequestState
I use IIS 7(not express) and asp.net mvc 3
Why it happen and how I can solve it?
I found the answer to my question here:
Session less MVC Controller for MVC 2 / RC (MSDN Blogs)
I hope it will help, if someone faces a similar problem.
For Asp.net Mvc project, I need to redirect every request to configuration page when user(should be admin of this website) visit this website at the first time. This operation like default login page(every request will be redirect to default login page if access denied).
After user config the configuration file, Route table will be mapped to normal controllers.
Ps. This page should helps Admin for detect error configuration and easy to deploy.
Update #1
I try to use ASP.NET MVC WebFormRouting Demo on Codeplex. But I can't redirect when user visit some existing page like "~/AccessDenied.aspx" or "~/web.config".
routes.MapWebFormRoute("RedirectToConfig", "{*anything}", "~/App_Config");
Thanks,
From your description, this appears to be an authorization concern, so I would recommend a custom Authorize attribute class (inherit from AuthorizeAttribute).
From here you can override the OnAuthorization method where you can check if the user has completed your required configuration steps and set the filterContext.Result accordingly. A basic implementation would look something like this (this assumes you have a valid /Account/Configure route):
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
var user = ; // get your user object
if(user.IsConfigured == false) // example
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{
"ConfigureUserRoute",
filterContext.RouteData.Values["ConfigureUserRoute"]
},
{"controller", "Account"},
{"action", "Configure"}
});
return;
}
}
}
You can find other examples of how to create a custom AuthorizeAttribute class here on StackOverflow.
2 ideas:
Use a catch-all rule on top of your routing table and put a constraint on it that checks for the config status
Put the code for this check in Application_BeginRequest in GlobalAsax
Details for the catch-all idea:
Create a rule with url "{*path}" and put it first in your list
Create a constraint to activate this rule only in case the configuration is not done yet
Create a simple controller e.g. ConfigController with a single action that does nothing but a RedirectToUrl("config.aspx")
But the solution in Application_BeginRequest would be simpler, since the whole code to handle this in one place
Now, I can apply technique from my another question to solve this problem. By keep some value in static instance when application is starting. Please look at the following code.
partial ConfigBootstapper.cs
public class ConfigBootstapper
{
public static EnableRedirectToConfigManager = false;
}
partial ConfigModule.cs
void HttpApplication_BeginRequest(object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
if (ConfigBootstapper.EnableRedirectToConfigManager)
{
app.Response.Redirect("~/App_Config");
}
}
partial Global.asax
protected void Application_Start()
{
[logic for setting ConfigBootstapper.EnableRedirectToConfigManager value]
}
PS. Don't forget to checking some condition that cause infinite-loop before redirect.