I have an ASP.NET MVC application which sets up an Autofac IOCContainer within the BeginRequest routine in Global.asax.
All is fine until one of my IIS7 UrlRwrite rules/conditions is met, e.g. lowercase url. I notice that as soon as I put an upper case letter within the url, so as the url rewrite rule is fired, the BeginRequest routine isn't reached, but the EndRequest routine is.
The problem I have with this is that the IOCContainer is never set in this scenario, so when EndRequest is fired and I attempt to dispose of the web context/ioc container, it errors as there it was never set in the first place.
Why does the BeginRequest never get reached in the above scenario, and is there a common workaround for the IOC issue that arises because of it?
The AutofacDependencyResolver that is part of the Autofac MVC integration already creates a new lifetime scope at the start of each HTTP request and will dispose it when the HTTP request ends. You should not need to manage this yourself. Do you have this problem when you use the DependencyResolver mechanism?
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
You can find out more about the MVC integration on the Autofac wiki.
http://code.google.com/p/autofac/wiki/Mvc3Integration
Related
Scenario:
I have an API with .net core 2.2
On top my controller I authorize access using IdentityServer4 with an Attribute
Inside one of my endpoints I want to authorize access to a method only in some cases
I implemented resource based authorization inside my endpoint just like it's shown in microsoft documentation.
It didn't work.
I put a breakpoint inside my authorization handler and tried debugging, but when this handler should be called, it is not.
I mean that when the following line runs
var authorizationResult = await _authorizationService
.AuthorizeAsync(User, Document, "EditPolicy");
the Handler should be called, but that never happens.
Did anyone have the same problem?
So in the end the problem was due to the registration of the service in the startup.cs.
I was using TryAddScope, by changing to AddScope it worked fine.
Situation
I have a Silverlight application that uses a WCF backend. Going forward we have moved to JS clients with WebAPI.
I have a couple of WebAPI controllers that I would like to use from the Silverlight client and so have them loaded within the ASP.Net application that hosts the WCF services.
This works fine from a "all services are available" point of view, however Authorization is being invoked multiple times for WCF calls; from OWIN and through WCF ServiceAuthorizationManager
On the WCF side, my ServiceAuthorizationManager implementation validates the token in the AuthHeader and then transforms that token (in the System.IdentityModel Claims Transformation sense). On the WebAPI side I'm using Thinktecture.IdentityModel which provides OWIN Middleware to do token validation and claims transformation .
Problem is, the OWIN middleware gets invoked for all requests (including the WCF requests). So in the case of a WCF request I get validation and transformation executed twice. I can't just remove the ServiceAuthorizationManager and let the middleware handle it, because WCF knows nothing of OWIN and the final step of the ServiceAuthorizationManager is to set the operation context principal (different to ClaimsPrincipal.Current).
Question
Has anyone had a problem like this before with WCF and WebAPI sitting side by side? Would the best approach be to somehow drop out of the OWIN pipeline very early on for WCF calls and if so how can that be done, through an OMC? Or can I somehow use the IAppBuilder.Map approach to only register the token validation and transformation components for API routes (in this case anything starting /api)?
I've managed to get this to work via a Branched Pipeline.
app.MapWhen(c => c.Request.Path.Value.Contains("/api"),
subApp =>
{
subApp.UseJsonWebToken(
issuer: clientDetails.Issuer,
audience: clientDetails.Audience,
signingKey: clientDetails.SigningKey);
subApp.UseClaimsTransformation(transformer.Transform);
var webApiConfig = WebApiConfig.Configure();
webApiConfig.DependencyResolver = StructureMapConfig.HttpDependencyResolver();
subApp.UseWebApi(webApiConfig);
});
Only thing I'm wondering is why IAppBuilder.MapWhen as above works, but when I use IAppBuilder.Map it doesn't seem to work...
app.Map("/api",
subApp => ...
Big thanks to the answer above. With this piece of code, I was able to figure out how to conditionally route calls so that WCF calls are not grabbed by static content middleware.
//app.UseMiddleware<ServeStaticFilesMiddleware>();
app.MapWhen(c => !c.Request.Path.Value.Contains(".svc"),
subApp =>
{
subApp.UseMiddleware<ServeStaticFilesMiddleware>();
});
Ok, my situation is much more complicated but there is an easy way to reproduce. Starting with a fresh new ASP.NET MVC 4 Web Application project and selecting Web API as a template I just add a second mvc action to the HomeController where I need to call Web API internally.
public async Task<string> TestAPI()
{
HttpServer server = new HttpServer(GlobalConfiguration.Configuration);
using (HttpMessageInvoker messageInvoker = new HttpMessageInvoker(server, false))
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:58233/api/values");
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = messageInvoker.SendAsync(request, new CancellationToken()).Result;
return await response.Content.ReadAsStringAsync();
}
//server.Dispose(); - if I do that on the second request I get a "Cannot access a disposed object." exception
}
that thing works only on the first request. On subsequent requests it throws with
The 'DelegatingHandler' list is invalid because the property
'InnerHandler' of 'RequestMessageHandlerTracer' is not null. Parameter
name: handlers
I really need to use the GlobalConfiguration.Configuration here since my system is very modular/plugin based, which makes it really hard to reconstruct that configuration within the action method(or anywhere else).
I would suggest trying to re-use the HttpServer instance on secondary requests. Creating and configuring a new server on every request is not an expected usage and you are likely hitting some edge case. Either setup a DI mechanism and inject into your controller a singleton of the HttpServer, or try accessing it from some static property.
I also would suggest using new HttpClient(httpServer) instead of HttpMessageInvoker.
The same issue can occur in Web API, if you have multiple HttpServers using the same configuration object, and the configuration contains a non-empty list of delegating handlers.
The error occurs because MVC/Web API builds a pipeline of handlers on first request, containing all the delegating handlers (eg RequestMessageHandlerTracer if request tracing is enabled) linked to each other, followed by the MVC server handler.
If you have multiple HttpServers using the same configuration object, and the config object contains delegating handlers, the first HttpServer will be successfully connected into a pipeline; but the second one won't, because the delegating handlers are already connected - instead it will throw this exception on first request/initialization.
More detail on the Web API case here (which is conceptually identical, but uses different classes and would have a slightly different fix):
webapi batching and delegating handlers
In my opinion, the MVC configuration classes should be pure config, and not contain actual delegating handlers. Instead, the configuration classes should create new delegating handlers upon initialization. Then this bug wouldn't exist.
Question: Why is there no session created (and session cookie) when an aspx page requested is pulled from cache?
Background Information
I've made quite a few google searches, but I can't find anything that indicates that this is the intended behavior. Our desired behavior is that a new session/cookie are always generated regardless of whether the page requested is pulled from cache.
We are caching pages using the following code. (.NET 3.5, IIS 7.5)
Response.Cache.SetExpires(DateTime.Now.AddMonths(1));
Response.Cache.SetCacheability(HttpCacheability.Server);
Response.Cache.SetVaryByCustom("IsLoggedIn");
Response.Cache.VaryByParams["*"] = true;
Response.Cache.SetValidUntilExpires(true);
Response.AddCacheItemDependency("Pages");
Any relevant information would be greatly appreciated.
In order to understand why an output-cached request will not create a session, you need to understand a little bit about the ASP.NET Application Life Cycle, the events . The basics are that are a defined series of HttpApplication events that may fire during each request. These events are typically subscribed to using event handlers by either HttpModule implementations or a Global.asax file.
Not every application event fires on every request, but the events that do fire will always fire in the defined order. The order of events that fire on IIS 7
1. BeginRequest
2. AuthenticateRequest
PostAuthenticateRequest
3. AuthorizeRequest
PostAuthorizeRequest
4. ResolveRequestCache
PostResolveRequestCache
5. MapRequestHandler (Integrated Mode Only)
PostMapRequestHandler
6. AcquireRequestState
PostAcquireRequestState
7. PreRequestHandlerExecute
<--- the IHttpHandler.ProcessRequest() method is called here
PostRequestHandlerExecute
8. ReleaseRequestState
PostReleaseRequestState
9. UpdateRequestCache
PostUpdateRequestCache
10. LogRequest (Integrated Mode Only)
PostLogRequest (Integrated Mode Only)
11. EndRequest
12. PreSendRequestHeaders
13. PreSendRequestContent
How an IHttpModule Works
The IHttpModule interface looks like:
public interface IHttpModule
{
void Init(HttpApplication context);
void Dispose();
}
The Init() method is used to subscribes event handlers to the application events and the Dispose() method cleans up after the module when the application is done with it.
How ASP.NET Sessions Work
System.Web defines an IHttpModule implementation named System.Web.SessionState.SessionStateModule. If the session is not disabled in the web.config, the following event handlers get wired up:
// app is the current HttpApplication ;
app.AddOnAcquireRequestStateAsync(this.BeginAcquireState, this.EndAcquireState);
app.ReleaseRequestState += this.OnReleaseState;
app.EndRequest += this.OnEndRequest;
Sessions work differently depending on what session mode is running but the key thing to understand is that sessions are retrieved and created in the SessionStateModule.BeginAcquireState method and that method is wired up asynchronously to the AcquireRequestState event.
How Output Caching Works
System.Web defines an internal IHttpModule implementation named System.Web.Caching.OutputCacheModule. The Init() method looks like:
void IHttpModule.Init(HttpApplication app)
{
if (RuntimeConfig.GetAppConfig().OutputCache.EnableOutputCache)
{
app.ResolveRequestCache += new EventHandler(this.OnEnter);
app.UpdateRequestCache += new EventHandler(this.OnLeave);
}
}
The OnEnter method evaluates the cache parameters and looks for a cached response that matches the request. The OnLeave method caches cacheable responses. The one thing you need to know is that if the OnEnter method is successful in retrieving a cached response, it calls the CompleteRequest() method on the HttpApplication instance. The documentation for this method reads: "Causes ASP.NET to bypass all events and filtering in the HTTP pipeline chain of execution and directly execute the EndRequest event". So, any events that occur between the time that CompleteRequest() is called and the EndRequest() event are skipped.
Why is session not created if page is cached?
The SessionStateModule subscribes SessionStateModule.BeginAcquireState to the application's AcquireRequestState event, so that is where sessions are created and retrieved.
The OutputCacheModule subscribes OutputCacheModule.OnEnter to the application's ResolveRequestCache event, so that is where cached responses are retrieved and where CompleteRequest() gets called. That means that the following events will never fire for a cached request: MapRequestHandler/PostMapRequestHandler; AcquireRequestState/PostAcquireRequestState; PreRequestHandlerExecute; IHttpHandler.ProcessRequest; PostRequestHandlerExecute; ReleaseRequestState/PostReleaseRequestState; UpdateRequestCache/PostUpdateRequestCache; and, LogRequest/PostLogRequest.
If a cached response is retrieved, then HttpApplication.CompleteRequest() is called, the AcquireRequestState event never fires, and the session is never created.
What to do about it?
Whether disabling output cache on pages in favor of controls is a viable solution depends on: (1) whether the code that uses the session is in the controls or in the pages; (2) how much load your application instance needs to be able to support; and, (3) what other caching mechanisms exist in either the application or its supporting infrastructure. Depending on your infrastructure, there may be other factors to consider. For example, ASP.NET sessions can perform and behave very differently depending on the session state mode and on whether the environment is load balanced or not. Also, if you happened to be running an HTTP accelerator cache like Varnish (for example), enabling sessions on a previously output-cached ASP.NET paged might change the behavior from omitting a session to attaching a stale session that belongs to a different user. This is just a hypothetical example, but the point is that there may be additional factors to consider when making these kinds of decisions.
Apparently this is default .NET behavior. If you create a new app, enable session, cache an aspx page, and then have a new user pull the cached version of that page, they will be given no session/cookie.
As a workaround...I think we'll just disable page outputcache and use more aggressive control caching.
I have an application that taps into BeginRequest and EndRequest to set up and tear down NHibernate sessions like this:
BeginRequest += delegate
{
CurrentSessionContext.Bind(SessionFactory.OpenSession());
};
EndRequest += delegate
{
var session = CurrentSessionContext.Unbind(SessionFactory);
session.Dispose();
Container.Release(session);
};
This works fine when deployed in IIS, until I check the "Require SSL" box. Once I do this, I get a NullReferenceException at session.Dispose().
I haven't debugged this yet and, yes, the fix is trivial, but I'm just curious about how "Require SSL" affects the lifecycle of a request. Is a session not set up on the server in these cases?
EDIT: Just to clarify, I'm referring to the "Require SSL" option in IIS configuration for the application, not the RequireHttps attribute for controllers.
This one piqued my curiosity so I dug into it a little; sorry for the necromancy.
I created a simple project that wired up notifications for every lifecycle event on the application object, and set breakpoints on each one.
It turns out that when "Require SSL" is set and you access without SSL, most of the events are completely skipped. The first event to fire is LogRequest, followed by PostLogRequest, EndRequest, PreSendRequestContent, and PreSendRequestHeaders (in that order). No other events are fired.
So your code was crashing because the BeginRequest event was never fired, and the EndRequest delegate tried to Dispose() something that had never been created.
What's interesting to me is figuring out why IIS behaves like this. I suspect the reason is that IIS still needs to log invalid connection attempts, as well as send content and headers, even if the requested resource requires SSL. Something has to generate that friendly "forbidden" page, after all. What I don't know is why EndRequest is called at all when they didn't bother calling BeginRequest; I'm guessing there's some IIS/ASP cleanup code that depends on it.
This behavior varies depending on whether the application pool is running in "Integrated" or "Classic" mode. In "Classic" mode, the ASP.NET events all fire "in between" the IIS PreRequestHandlerExecute and PostRequestHandlerExecute events. You didn't say which you were running, but it has to be Integrated; otherwise you'd have seen the behavior you were expecting, i.e. none of your code would have executed at all.
Interestingly, if you try to subscribe to the LogRequest, PostLogRequest, or MapRequestHandler events when in Classic mode, you get a runtime exception; these only "make sense" in the context of the integrated pipeline.
Here is something that might help you
ASP.NET Application Life Cycle Overview for IIS 5.0 and 6.0
ASP.NET Application Life Cycle Overview for IIS 7.0
You might want to do what you are doing in global.asax instead.