We have a web app that displays data from a couple of different web apps. In each of those web apps, the culture is configurable (it is set by a company admin at the app level for all users, not per user).
In our web app, we need to display the data from each of the other web apps, in the culture configured for that app. The only good news is that currently we don't display "mixed" data from both apps, in a single view. And generally, a controller and all of its actions will only work with data from a single app (meaning that all actions from that controller will use the same culture).
There are several things I'm struggling with:
Does .NET Core really not honor setting culture like in the old days? I've tried it but it doesn't work (unless I'm setting it in the wrong place in the pipeline).
Thread.CurrentThread.CurrentCulture = firstWebAppCultureInfo;
Thread.CurrentThread.CurrentUICulture = firstWebAppCultureInfo;
Does it really only check these to find and set the culture?
QueryStringRequestCultureProvider
CookieRequestCultureProvider
AcceptLanguageHeaderRequestCultureProvider
I know I could write a custom provider, but that wouldn't seem to help me. We already get the various culture settings for the apps, from a database and store them in session.
My first thought was to write out a Culture Cookie every time the user hits a controller. I'd read the culture from session and write the cookie. But there are a couple of issues I see with this.
Issue 1: If I write the cookie out in the controller action, it doesn't "take affect" the first time it is changed from one culture to another. Obviously the cookie has already been read and the culture has already been set for that request. I'm too late in the pipeline to do that.
I tried writing the cookie out in the constructor of the controller (or constructor of a base controller) but HttpContext is not yet set at that point (it is null).
I have worked a bit with Filters and I will write an Action Filter that writes the cookie to see if that works, but see Issue 2.
Issue 2: Another issue would surface if the user opened multiple instances of their browser. The last culture written will "win" I suspect. So if a user is displaying data in Culture fr-FR in one browser and en-US in another instance of a browser and navigates to a controller/view that writes that en-US cookie, and then from the other browser instance navigates to a page that is supposed to use the fr-FR culture, the user would get the en-US setting from the cookie and that's not acceptable.
I don't really want to use the QueryStringRequestCultureProvider, because having that show up in the url is ugly.
I'm definitely not an expert in this area.
What am I missing?
How would you all implement this?
Thanks in Advance.
UPDATE
I created an ActionFilter and TypeFilterAttribute that allows me to pass a parameter into the ActionFilter. Using the parameter I can decide which key to read from Session to give me the culture setting I've stored for each web app.
From there I can mark each controller with that attribute and the appropriate parameter to tell the action filter what culture to use (remember we don't mix data, so each controller displays views that show data from only one of the two web apps, not both).
In the ActionFilter I can look up the culture string from session and do the following and it works:
var ci = new CultureInfo(this.cultureSetting);
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
While that is encouraging, it feels hacky. So please comment on the solution...
I'd like to find a way to do all of this in a BaseController or somewhere, so I wouldn't need to put attributes on all of our controllers.
But whatever I've tried in a base controller seems like it is either:
a) too late in the pipeline to set the culture for that request, or
b) too early in the pipeline to get access to httpcontext.
Thoughts? Ideas?
UPDATE 2
I guess overriding OnActionExecuting in my base controller lets me do the same things as the ActionFilter...
So I guess I have a solution, but if anyone has a better idea, I'm listening.
We decided to go with an ActionFilter for our solution, so we can mark Controllers and/or Actions with an Attribute that indicates which app we are getting data from, so we can apply that app's culture setting...
Related
Related to that question.
I've understood that I have to create some services for handle my entity and so on. That's because I have to "move" my logic away from controllers and place into "managers" (i.e. services)
Now I have a service that have some logic into it. In that service I, depending on user, return a list of "associated object" - say that those object are sport's team.
Let's say that first element of my list (generated from a repository somehow) is the "default" team and say that I have a page were I can change it FOR all session long.
After log out or sessions stale, I want to return at "default" situation.
So my idea was: "since I've wrote a manager for this entity, I'll write a private attribute in this class where load (from db) this property and store (temporarily, with setter method) my changes."
This doesn't affect my db and I can keep my information for all session long.
But a thought came into my mind: how about session object? (is a service, if I didn't understood wrong)
Is my solution a good solution, or is better to store my information into session object?
From my point of view it's the same except that I can read session's variables directly from twig by using app.session. Am I wrong?
Moreover, if I'm not wrong, how can I access my object properties from twig without each time pass them from controller? (is much like having a global variable that I want to display everywhere into my application pages).
Edit:
More information can be found in this chat transcript.
If you want to store a variable for the duration of a session (for example, login until logout or as long as the user doesn't close his browser window) you have to store it in the session object. If you want to store a variable for the duration of a request, you can store it in the manager service.
However, you can use the manager service to load the session variable and make it available to the controller.
Just like it is a good idea to decouple the controller from the database/Doctrine it is also a good idea to decouple the controller from the session.
Update: As mentioned in the comments when looking at REST it is not a good idea to do the session stuff in the service. However, you should still store the variables in the session and use the controller to set the value in the service.
I'm finding that in the current application I'm working with, I'm retrieving several entities (related to the authenticated users account) in almost every controller. These entities are cached at the orm layer however, it seems that these entities would be a good candidate to load once at authentication time and add a few properties to the applications custom IPrincipal object.
Another option I was thinking of was creating a custom context object (with the users related account objects) and passing it around with the current request.
Benefits / drawbacks to either approach? Is there another way of dealing with commonly used objects like this?
It sounds like you miss the fact that the instance of IPrincipal/IIdentity is recreated upon every request. It is not persisted anywhere if you not persist it in an explicit way.
I don't think then there's performance difference between a custom principal class holding the data vs a cached ambient property.
On the other hand, the drawback of a custom authentication classes is that you have to provide a custom authentication module so that these instances are recreated during AuthenticateRequest event in the processing pipeline. In other words, you'd have to replace FormsAuthenticationModule with your own one. This is not difficult but I wouldn't do this if it is not absolutely necessary.
Note also that some data can be persisted in the UserData section of the forms cookie. This means that you can have it as long as the cookie is valid and create it only once.
I'm managing a rather large project, written in asp.net webforms + mvc3, with a large user base, and a pretty high daily visitor count. Basically, there are a lot of requests at any given moment.
One of my controllers in MVC that handles / resizes images on the fly has the following attribute applied to it:
[SessionState(SessionStateBehavior.Disabled)]
Now, if an action in the controller tries to access the session - it obviously throws an exception - so we're good so far.
The problem is: if I go to the IIS Worker Processes window (Win Server 2008 R2, IIS 7.5), and check the current requests for this site, I can sometimes see the requests to an action in this controller. Their current state is locked in State: RequestAcquireState, Module Name: Session. Sometimes these locks go over a second or two in this state.
Wasn't the whole point of the attribute in the first place to make the requests to the controller ignore the state, and not waste time (and possibly being locked) trying to acquire the state?
If this is so - am I doing something wrong here, or does the problem lie elsewhere?
[migrated from comments]
If you're using a custom controller factory or route handler, make sure that they're aware of the controller's session state behavior. Marking a controller as not requiring session state requires cooperation from both of these components. Out-of-box, the DefaultControllerFactory and MvcRouteHandler are designed to work with this. See DefaultControllerFactory.GetControllerSessionBehavior and MvcRouteHandler.GetHttpHandler for more information. If you're writing custom components, you can use those methods as inspiration.
I'm making a .net component, resulting in a dll assembly, that will be referenced by an asp.net site, and in another project by an asmx webservice. All code is version 2.0.
The component has a few functions that call an external webservice, which returns an object. One of these functions is for example Login(username, password), which generates amongst others a unique session id for this user. The resulting information and session id are stored in variables in the component.
The issue in the website is:
When the user logs in in the frontend, and my component gets called and checks the login and generates the session id, I want that information in my component to persist when the user browses to another page in the site.
The issue in the web service using my component is:
I don't really care much about session state, but I want the component to just work. If per call a new session id is generated, that's okay.
The combination of the two environments causes the following problem for me:
I can't use the asp.net Session() variable in my component without referencing system.web, which is kinda silly and might not be available in the web service project that includes my component
I can't program my component as a singleton, because then in the website application, it's shared amongst all visitors, overwriting sessions and whatnot
making an array of "session information" in my component and maintaining that is hard to program (when does it get invalidated?) and might not work in a web farm environment
Is there a solution to this situation? Or should I make two instances of my component, one for use in websites, and one for use in web services?
Perhaps I'm not understanding what your asking but why can't you do something like:
Component.Login(user,pass);
Session["Component"] = Component.SessionID
I've created an extra class "Factory" which has a .Create(byref Session as HttpSessionState), that looks if the passed in session object is Nothing, if not, it checks if there is a Component object in it, if so, it uses it, if not, it creates it and adds it to the session.
In the case of the webservice call, the factory gets the Nothing-parameter, so it doesn't check in the session object. It seems to work.
Thanks for your answers!
I'm having trouble figuring out how to access a cookie from a compiled object. I'm trying to make a compiled (DLL) object that will check the users cookie and then compare that to a database to confirm they have the correct access.
I can pass in the cookie info fine and the component will work, but I'm trying to have the component check the users cookie as well. I'm not even sure what object to use. I've been searching all weekend and I've seen references to httprequest, httpcookie, cookie, and cookiecollection.
I can look up cookie values on the page itself using Request.Cookies("inet")("user_id") but this doesn't work in the component.
Objects (App_Code/ compiled dlls) can only access Request via the static HttpContext.Current object
HttpCookie cookie = HttpContext.Current.Request.Cookies["CookieName"];
(If it's not called from a web app, HttpContext.Current is null, so you may want to check for that when running in unit testing)
(If this isn't App_Code, you'll need to reference System.Web)
If the component is a separate DLL from your web app you'd need to pass in a reference to the Request object.
That said why not just read/check the cookie value in your ASP.NET code before calling into your DLL. It's not such a good idea to have your business logic coupled to your web tier like this.