I started with a WebForms .NET 4.51 application. I then added WebAPI to the same application. In Session_Start() I create a variable instance that I store within the session as follows:
public class Global : HttpApplication
{
protected void Session_Start(object aSender, EventArgs aEventArgs)
{
//Create an object to hold all the settings for the user in the session. This is only loaded once we
//have a user successfully logged in
HttpContext.Current.Session[SYSTEM_SETTINGS_SESSION_KEY] = new SystemSettings();
}
}
and I have a simple property accessor as follows:
public class Global : HttpApplication
{
public static SystemSettings SystemSettings
{
get
{
if (HttpContext.Current == null)
return null;
return HttpContext.Current.Session[SYSTEM_SETTINGS_SESSION_KEY] as SystemSettings;
}
}
}
The above all works well when I am accessing the property from code except when I attempt to do this from within a WebAPI controller as follows via the property from above viz Global.SystemSettings:
public class EmailActivitiesController : ApiController
{
emailBody = EmailToClientsTemplateBuilderHelper.TemplateContentBuild(emailBody, Global.SystemSettings);
}
When I inspect HttpContext.Current.Session it is NULL.
So why is the Session collection null when accessed from the WebAPI controller?
I need to store use the information related to the user's session in the WebAPI controller, so do I need to store things differently now?
UPDATE
The accepted solution also worked for WebAPI 1 which is what the application is using.
This is because WebApi does not have Session enabled by default. WebApi is trying to encourage you to move towards stateless HTTP and RESTful APIs.
I strongly urge you to rework your design without sessions.
With this said, you can enable Session in WebApi 2 by adding this to Global.asax
protected void Application_PostAuthorizeRequest()
{
System.Web.HttpContext.Current.SetSessionStateBehavior(
System.Web.SessionState.SessionStateBehavior.Required);
}
Related
There seems to be some weird behavior in ASP.NET Web API (4.0.30506) that I haven't witnessed before.
What I'm seeing is that the same action filter attribute instance is reused over Web API requests. This is especially a problem in case this attribute gets dependencies injected to it, since those dependencies might be specific to the web request. I'm aware that it's better for attributes to be passive, but my assumption was that action filter attributes where not cached.
I searched for any articles, blog posts or Microsoft change logs that described this and the reason behind this, but I couldn't find a single thing. That makes me wonder whether there is something wrong with my configuration that makes this happening. Thing is however, that I'm able to reproduce this issue in a new and empty Visual Studio 2012 Web API project.
What I did was create a new empty project using the Visual Studio 2012 ASP.NET MVC 4 Web Application project with the "Web API" template. It comes with the Web API 4.0.20710.0 NuGet package. After that I added the following attribute:
[DebuggerDisplay("{id}")]
public class TestFilterAttribute : System.Web.Http.Filters.ActionFilterAttribute {
private static readonly List<int> used = new List<int>();
private static int counter;
private readonly int id;
public TestFilterAttribute() {
this.id = Interlocked.Increment(ref counter);
}
public override void OnActionExecuting(HttpActionContext actionContext) {
// Just for testing: I would expect this line never to throw, but it does.
if (used.Contains(this.id)) throw new Exception("WAT?");
used.Add(this.id);
base.OnActionExecuting(actionContext);
}
}
And I add this attribute to the ValuesController (part of the default template):
public class ValuesController : ApiController {
// GET api/values
[TestFilterAttribute]
public IEnumerable<string> Get() {
return new string[] { "value1", "value2" };
}
// ...
}
Now when I start the project, go to the /api/values in the browser and refresh that page a few times, the "WAT?" exception is thrown.
Is this normal behavior of Web API and if so, what's the reasoning about this? Or Did I miss some memo about this change somewhere? Does this make Web API attributes extra unsuited for doing dependency injection? Or am I'm doing something wrong?
Web API is built on top of MVC, thus it uses a lot of it's functionality.
Attribute instance re-usability is part of the aggressive caching introduced by MVC 3. This means that the same Attribute instance will most likely be used with all the Actions it is applied on. MVC pipeline will do it's best at trying to treat your Attribute class like a Singleton.
Because the same Attribute instance is reused, it's Constructor is not called and id is not incremented. If, for example, you increment id inside OnActionExecuting, all will work well.
You can still do everything you want with your Attribute. You only need to keep in mind that you are not guaranteed to always get a new instance created. The constructor shouldn't contain anything but initial initialization.
public TestFilterAttribute() {
// Instance will be reused thus this will not be called for each Action
}
public override void OnActionExecuting(HttpActionContext actionContext) {
// Called on each Action
}
I need to store the current user name for every save request/user in SQL table. It is MVC 4 based application and hosted in IIS server. Also, it is an internal tool and NTLM based authentication.
I have got the username of current user using HttpContext.Current.User.Identity.Name My question is how the global variable reacts in MVC? What would happen for "currentUser" variable when multiple requests comes? Will the currentUser creates new value for every requests? Please help me to understand.
Sample Code:
public class ClearCacheController : ApiController
{
private string currentUser = HttpContext.Current.User.Identity.Name.ToLower();
public void method1()
{
SaveValue1(currentUser);
}
public void method2()
{
SaveValue2(currentUser);
}
public void method3()
{
SaveValue3(currentUser);
}
}
Controllers are instantiated and disposed for each unique request. So, something like User.Identity.Name is not really a global variable. It's an instance variable on the controller. (Well, User is an instance variable on the controller, while Identity is an instance variable on User, etc.). Long and short, it will only hold the value of the user who made the particular request being executed, not any user making any request.
I am Using Web Api 2.1 with Asp.Net Identity 2. I am trying to get the authenticated User on my ApiController's constructor (I am using AutoFac to inject my dependencies), but the User shows as not authenticated when the constructor is called.
I am trying to get the User so I can generate Audit information for any DB write-operations.
A few things I'm doing that can help on the diagnosis:
I am using only app.UseOAuthBearerTokens as authentication with Asp.Net Identity 2. This means that I removed the app.UseCookieAuthentication(new CookieAuthenticationOptions()) that comes enabled by default when you are creating a new Web Api 2.1 project with Asp.Net Identity 2.
Inside WebApiConfig I'm injecting my repository:
builder.RegisterType<ValueRepository>().As<IValueRepository>().InstancePerRequest();
Here's my controller:
[RoutePrefix("api/values")]
public class ValuesController : ApiController
{
private IValueRepository valueRepository;
public ValuesController(IValueRepository repo)
{
valueRepository = repo;
// I would need the User information here to pass it to my repository
// something like this:
valueRepository.SetUser(User);
}
protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
// User is not avaliable here either...
}
}
But if I inspect the User object on the constructor, this is what I get:
The authentication is working, if I don't pass my token, it will respond with Unauthorized. If I pass the token and I try to access the user from any of the methods, it is authenticated and populated correctly. It just doesn't show up on the constructor when it is called.
In my WebApiConfig I am using:
public static void Register(HttpConfiguration config)
{
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// ... other unrelated injections using AutoFac
}
I noticed that if I remove this line: config.SuppressDefaultHostAuthentication() the User is populated on the constructor.
Is this expected? How can I get the authenticated user on the constructor?
EDIT:
As Rikard suggested I tried to get the user in the Initialize method, but it is still not available, giving me the same thing described in the image.
The problem lies indeed with config.SuppressDefaultHostAuthentication().
This article by Brock Allen nicely explains why that is. The method sets the principal intentionally to null so that default authentication like cookies do not work. Instead, the Web API Authentication filter then takes care of the authentication part.
Removing this configuration when you do not have cookie authentication could be an option.
A neat solution as mentioned here, is to scope the Web API parts of the application, so that you can separate out this configuration to a specific path only:
public void Configuration(IAppBuilder app)
{
var configuration = WebApiConfiguration.HttpConfiguration;
app.Map("/api", inner =>
{
inner.SuppressDefaultHostAuthentication();
// ...
inner.UseWebApi(configuration);
});
}
Don't know if this is still relevant, but I've had exactly the same problems, as you've described above. I've managed to solve it using custom OWIN middleware component.
Some info about my application structure:
Using MVC WebApp and WebAPI in same project (probably not the best option, but I have no time to change it, since deadline is approaching ;))
Using AutoFac as IoC container
Implemented custom ICurrentContext to hold information about currently logged on user (with CookieAuth in MVC and Bearer Token Auth in WebAPI), which is injected where needed (controllers, BAL objects, etc.)
Using EntityFramework 6 for Db access
Converted ASP.NET Identity to use int keys rather than string (http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity)
So on to the code. This is my ICurrentContext interface:
public interface ICurrentContext
{
User CurrentUser { get; set; } // User is my User class which holds some user properties
int? CurrentUserId { get; }
}
and implementation of it:
public class DefaultCurrentContext : ICurrentContext
{
public User CurrentUser { get; set; }
public int? CurrentUserId { get { return User != null ? CurrentUser.Id : (int?)null; } }
}
I've also created an OWIN middleware component:
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.Owin;
namespace MyWebApp.Web.AppCode.MiddlewareOwin
{
public class WebApiAuthInfoMiddleware : OwinMiddleware
{
public WebApiAuthInfoMiddleware(OwinMiddleware next)
: base(next)
{
}
public override Task Invoke(IOwinContext context)
{
var userId = context.Request.User.Identity.GetUserId<int>();
context.Environment[MyWebApp.Constants.Constant.WebApiCurrentUserId] = userId;
return Next.Invoke(context);
}
}
}
Some information about this component: MyWebApp.Constants.Constant.WebApiCurrentUserId is some string constant (you can use your own) that I've used to avoid typos since its used in more than one place. Basicly what this middleware does, is that it adds current UserId to the OWIN environment dictionary and then Invokes the next action in pipeline.
Then I've created Use* extension statement to include OMC (OWIN Middleware Component) into OWIN pipeline:
using System;
using Owin;
namespace MyWebApp.Web.AppCode.MiddlewareOwin
{
public static class OwinAppBuilderExtensions
{
public static IAppBuilder UseWebApiAuthInfo(this IAppBuilder #this)
{
if (#this == null)
{
throw new ArgumentNullException("app");
}
#this.Use(typeof(WebApiAuthInfoMiddleware));
return #this;
}
}
}
To use this OMC, I've put the Use* statement right after Use* statement for Bearer token inside my Startup.Auth.cs:
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions); // This was here before
// Register AuthInfo to retrieve UserId before executing of Api controllers
app.UseWebApiAuthInfo(); // Use newly created OMC
Now the actual usage of this principle was inside AutoFac's Register method (called on some bootstrap code at the start of web application; in my case this was inside Startup class (Startup.cs), Configuration method) for my ICurrentContext implementation which is:
private static void RegisterCurrentContext(ContainerBuilder builder)
{
// Register current context
builder.Register(c =>
{
// Try to get User's Id first from Identity of HttpContext.Current
var appUserId = HttpContext.Current.User.Identity.GetUserId<int>();
// If appUserId is still zero, try to get it from Owin.Enviroment where WebApiAuthInfo middleware components puts it.
if (appUserId <= 0)
{
object appUserIdObj;
var env = HttpContext.Current.GetOwinContext().Environment;
if (env.TryGetValue(MyWebApp.Constants.Constant.WebApiCurrentUserId, out appUserIdObj))
{
appUserId = (int)appUserIdObj;
}
}
// WORK: Read user from database based on appUserId and create appUser object.
return new DefaultCurrentContext
{
CurrentUser = appUser,
};
}).As<ICurrentContext>().InstancePerLifetimeScope();
}
This method is called where I build AutoFac's container (hence the input parameter of type ContainerBuilder).
This way I got single implementation of CurrentContext, no matter how user was authenticated (via MVC Web Application or Web API). Web API calls in my case were made from some desktop application, but database and most of codebase were the same for MVC App and Web API.
Don't know if its the right way to go, but it has worked for me. Although I am still a little concerned how would this behave thread-wise, since I don't know exactly how using HttpContext.Current inside API calls would behave. I've read somewhere that OWIN Dictionary is used per-request basis, so I think this is safe approach. And I also think that this isn't so neat code, but rather a little nasty hack to read UserId. ;) If there's anything wrong with using this approcah, I'd appreciate any comment regarding it. I've been strugling with this for two weeks now and this is the closest I got of getting UserId in one place (when resolving ICurrentContext from AutoFac through lambda).
NOTE: Wherever there is usage of GetUserId, it can be replaced with original GetUserId (which returns string) implementation. The reason I'm using GetUserId is because I've rewritten ASP.NET to some extent for using ints instead of strings for TKey. I've done this based on following article: http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity
The User property of the controller is not populated until the Initialize method is called which happens after the constructor is invoked, hence thats why the Identity is not yet populated with the authorzied user data.
I realized that removing config.SuppressDefaultHostAuthentication() allowed me to get the Identity in the constructor much earlier. However, I wouldnt suggest doing this if you are using Token Authentication.
Thread.CurrentPrincipical is available throughout the pipeline, you could skip the User registration below:
valueRepository.SetUser(User);
and access
Thread.CurrentPrincipical
In the repository instead, making the repository context aware. Furthermore, you could add a context layer.
If nothing of the above solutions work try this one:
public ActionResult GetFiles()
{
...
string domainID = System.Web.HttpContext.Current.Request.LogonUserIdentity.Name;
...
}
I am using MVC 4 Web API to create a service layer for an application. I am trying to create a global filter that will act on all incoming requests to the API. Now I understand that this has to be configured differently than standard MVC global action filters. But I'm having issues getting any of the examples I'm finding online to work.
The problem I am running into is in registering the filter with Web API.
I have my Global.asax set up like this...
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
MVCConfig.RegisterRoutes(RouteTable.Routes);
MVCConfig.RegisterGlobalFilters(GlobalFilters.Filters);
WebApiConfig.RegisterRoutes(GlobalConfiguration.Configuration);
WebApiConfig.RegisterGlobalFilters(GlobalConfiguration.Configuration.Filters);
}
}
My standard Mvc routing and filters work correctly. As does my WebApi routing. Here is what I have for my webApi filter registration...
public static void RegisterGlobalFilters(System.Web.Http.Filters.HttpFilterCollection filters)
{
filters.Add(new PerformanceTestFilter());
}
And here is the PerformanceTestFilter...
public class PerformanceTestFilter : ActionFilterAttribute
{
private readonly Stopwatch _stopWatch = new Stopwatch();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_stopWatch.Reset();
_stopWatch.Start();
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
_stopWatch.Stop();
var executionTime = _stopWatch.ElapsedMilliseconds;
// Do something with the executionTime
}
}
This filter works fine when it is registered with the standard Mvc GlobalFilterCollection, but when I try to register it with System.Web.Http.Filters.HttpFilterCollection I get an error saying that it is not assignable to parameter type System.Web.Http.Filters.IFilter.
So I'm assuming that my PerformanceTestFilter needs to inherit from something other than ActionFilterAttribute in order to be registered as a webapi filter. I'm just not sure what that needs to be.
I imagine I will need to create two individual filters to work with mvc and webapi respectively. If there is a way to create a filter that could be registered to both, that would be great. But my primary concern is simply to get it working for webapi.
Thanks
The following should work. We actually use this for our web API project.
GlobalConfiguration.Configuration.Filters is of type HttpFilterCollection
var filters = System.Web.Http.GlobalConfiguration.Configuration.Filters;
filters.Clear();
filters.Add(new ValidationActionFilterAttribute());
public class ValidationActionFilterAttribute : FilterAttribute, IActionFilter, IFilter
{
...
}
Also, if you're working in a project that contains both MVC and WebAPI assembilies, could you check what's the namespace your
ActionFilterAttribute's namespace. It's fairly confusing cause there
are two ActionFilterAttributes under both:
System.Web.Http.Filters
System.Web.Http.Mvc
Source: Why is my ASP.NET Web API ActionFilterAttribute OnActionExecuting not firing?
It appears that you will need to have two filters, one for API and one for MVC. You can factor the common code into a separate class, and then just use the specific filter to call through to your common class, thus not violating DRY and essentially using the actual filters as wrappers which can be registered as filters.
I'm relatively new to ASP.NET MVC so I'm having a little trouble with this one. When the application start, I set a property to a certain value. Yet when I try to access this property in a controller (using this), the property is always null.
Is it something I am misunderstanding?
Here a simplified version of the code :
public class MvcApplication : System.Web.HttpApplication
{
internal ExtensionManager Extensions { get; private set; }
protected void Application_Start()
{
this.Extensions = new ExtensionManager();
}
}
public ActionResult ListExtension()
{
var app = HttpContext.ApplicationInstance as MvcApplication;
return View(app.Extensions.InstalledExtensions);
}
There are multiple HttpApplication instances created throughout the application lifecycle but Application_Start is only called once when the application first initialises.
See this for more info: http://msdn.microsoft.com/en-us/library/ms178473%28VS.80%29.aspx
I don't know what you are trying to store here (extensions?) but if its global to all users then you could use a static property, otherwise store it somewhere else.
Late answer (but for anyone in need of assistance).
I had this issue as well.
I think you can use the Application["myKey"] array, to set some values. Sure they aren't instance properties, but you can set a dependency injection container (like unity, who recommends this option in their code sample) , then access it from your controller with Application["myKey"].
You should set only static data during application start. Do not set
any instance data because it will be available only to the first
instance of the HttpApplication class that is created.