Persisting/caching data between requests - common approach - asp.net-mvc

I'm developing an Asp.net (MVC but this doesn't really matter) application. I have a custom IHttpModule that's responsible for the PostAuthenticateRequest to change user principal & identity.
I'm storing UserID and UserName in authentication cookie when user logs-in. I have an IUser (implemented by DAO and Business Objects layer, each with their own additional members) that I need all over Business Service classes. When a user wants anything I have to provide IUser object instance (usually from Business Objects layer) so providing ID from the auth ticket isn't sufficient.
So I'm thinking of how and where would be best to persist logged in user's IUser data?
I don't want to fetch it every time from the DB (based on authentication ticket's UserID data)
I can't store it in Session since I have to work inside PostAuthenticateRequest, where Session isn't ready yet
I want all the functionality to be encapsulated within my custom IHttpModule
Choices that I see:
Cache
Cookie
(Session) - by moving from PostAuthenticateRequest to PostAcquireRequestState event and change principal/identity there, but I'd like to avoid this
Processes that seem to complicate things are:
User logs-in, user data is fetched from the DB and persisted somehow for later requests
User logs-out, user data has to be removed from persisted medium automagically
User changes own profile, user data has to be discarded and reread on next request from the DB
I wan't all these to be handled automatically by HttpModule (if possible) to eliminate developer's errors of forgetting to reset these things.
What I also don't want is to write/read some hardcoded variables/keys and manipulate them in other parts of the application. This would only present technical debt.
Questions
What would you suggest?
How does SO persist user data between requests?

Given your requirements, I suppose the best solution is to retrieve the ID from the cookie and use it to index into the Http Cache (HttpContext.Current.Cache).
If you want to maintain how users access it, wrap the Cache in a "UserCache" object. The object could be constructed by an HttpModule and stored as a (wait for it...) singleton within the cache itself or, better yet, just constructed when needed to pull from the http cache. This would depend on where you need to access it and whether HttpContext.Current.Cache is directly available. The lazy implementation is below.
Again, this is for clarity and is not how I'd actually implement it.
public class UserCache
{
public IUser GetUser(object userKey)
{
return HttpContext.Current.Cache[userKey];
}
public void AddUser(object userKey, IUser user)
{
/* this could pull the key from the user object as well. */
HttpContext.Current.Cache.Add(/* add the object with key and a sliding expiration that is slightly greater than session timeout */);
}
public void ExpireUser(object userKey)
{
HttpContext.Current.Cache.Remove(userKey);
}
/* If you don't want to do SQL cache dependency */
public void UpdateUser(object userKey, IUser user)
{
HttpContext.Current.Cache.Insert(/* ... */);
}
}
Using the default caching mechanisms (or better yet a caching mechanism supplied by DI so you're not tied to an implementation), you can set an expiration to automatically remove users from the cache as mentioned in the comment. You can setup the cache to be dependent on SQL server updates as well to handle the updates or manually update it as part of the service to save changes.
More information about the default cache is available here. More information about cache dependencies is available here.
In the HttpModule itself, I suppose you could do some magic in the EndRequest event to see if the request is authenticated and then log the user out based on the cookie, but I'm not sure if that would work as I've never tried it. You might want to have a look at this article on MSDN from WAY back in the 1.1 days and see if it answers some of the problems you are trying to solve.
As for the SO architecture and how they do it, I'd imagine they load it when needed because they keep most of the database in RAM at all times (http://highscalability.com/stack-overflow-architecture).

Related

How can I access lookup data via an injected service from within an entity?

My application is using DDD with .NET Core and EF Core. I have some business rules that run within an entity that need to check dates against a cached list of company holiday dates. The company holidays are loaded from the db and cached by an application service that is configured with our DI container so it can be injected into our controllers, etc.
I cannot determine how, or if it's the right/best approach, to get the service injected into the entity so it can grab those dates when running business rules. I did find this answer that appears to demonstrate one way to do it, but I wanted to see if there were any additional options because that way has a bit of a code-smell to me upon first glance (adding a property to the DbContext to grab off the private constructor injected context).
Are there any other ways to accomplish something like this?
ORM classes are very rarely your domain objects. If you can start with your domain and seamlessly map to an ORM without the need for infrastructure specific alterations or attributes then that is fine; else you need to split your domain objects from your ORM objects.
You should not inject any services or repositories into aggregates. Aggregates should focus on the command/transactional side of the solution and work with pre-loaded state and should avoid requesting additional state through any handed mechanisms. The state should be obtained and handed to the aggregate.
In your specific scenario I would suggest loading your BusinessCalendar and then hand it to your aggregate when performing some function, e.g.:
public class TheAggregate
{
public bool AttemptRegistration(BusinessCalendar calendar)
{
if (!calendar.IsWorkingDay(DateTime.Now))
{
return false;
}
// ... registration code
return true;
}
// or perhaps...
public void Register(DateTime registrationDate, BusinessCalendar calendar)
{
if (!calendar.IsWorkingDay(registrationDate))
{
throw new InvalidOperationException();
}
// ... registration code
}
}
Another take on this is to have your domain ignore this bit and place the burden on the calling code. In this way if you ask you domain to do something it will do so since, perhaps, a registration on a non-working day (in my trivial example) may be performed in some circumstances. In these cases the application layer is responsible for checking the calendar for "normal" registration or overriding the default behaviour in some circumstances. This is the same approach one would take for authorisation. The application layer is responsible for authorisation and the domain should not care about that. If you can call the domain code then you have been authorised to do so.

Get the current session with spring-session

Here's my question: I'm writing a platform which I will be giving to the customers to implement their projects with. So in my platform I have created a SessionService in which I have methods like getCurrentSession, getAttribute, setAttribute, etc. Before spring-session my getCurrentMethod looked like this:
#Override
public HttpSession getCurrentSession() {
if (this.session == null) {
final ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return attr.getRequest().getSession(true); // true == allow create
}
return this.session;
}
which worked perfectly fine, although it looks ugly and have no backing like redis. Now I want to migrate to spring-session and I was hoping to use the SessionRepository to find the current session of the user, however I can only see a getSession(String id) in there. I believe the id is stored in the cookie, so to use it I will probably have to pass the HttpServletRequest object from my controllers, to my facades, to the service layer which is very near the db layer. This looks like a very bad idea to me, so my question would be: is there any way to get the currentSession near the db layer? One way I would think is to write an interceptor that will be invoked the controllers, which will set the current session in the repository, or the service maybe? I'm just not sure this is the right way to go.
Obtaining the Session Id from Service Layer
You can use the RequestContextHolder to retrieve the session id, set attributes, and remove attributes.
The RequestContextHolder is typically setup using RequestContextListener or RequestContextFilter. Spring Session does NOT work with RequestContextListener because there is no way for Spring Session to wrap the request before the RequestContextListener is invoked.
Unfortunately, this means for Spring Boot applications, RequestContextHolder does not work out of the box. To work around it you can create a RequestContextFilter Bean. See spring-boot/gh-2637 for updates on this issue.
Should I be putting this in session?
Just because it is easy to put a lot of objects in session and it is stored in Redis does not mean it is the right thing to do.
Keep in mind that the entire session is retrieved on every request. So while Redis is fast, this can have a significant impact if there are lots of objects in session. Obviously the implementation can be optimized for your situation, but I think the concept of session generally holds this property.
A general rule of thumb is, "Do I need this object for over 95% of my requests?" (read this as almost all of my requests). If so, it may be a candidate for session. In most cases, the object should be security related if it fits this criteria.
Should I access session id from ThreadLocal in the service layer?
This is certainly open for debate as code is as much of an art as it is a science.
However, I'd argue that you should not be obtaining the session id from thread locale variables throughout your architecture. Doing this feels a bit like obtaining a "Person id" and obtaining the current "Person id" from the HttpServletRequest in a ThreadLocale. Instead, values should be obtained from the controller and passed into your service layer.
Your code does not need changing. It will return the Spring Session session object.
Though it is generally better to inject the HttpSession from the controller, or use session-scoped beans and #SessionAttribute than to have such a session service in the first place.

Where does the Grails Spring Security plugin save currentUser in HttpSession

it is famous to get the current user by calling :
springSecurityService.currentUser ;
Does Spring Ssecurity API save this object in HttpSession. if So, how to access to this object from session .
i.e: session['currentUser']
It doesn't.
As you showed in your answer the Principal is stored in the session, but that's the org.springframework.security.core.userdetails.UserDetails instance that was created by the org.springframework.security.core.userdetails.UserDetailsService. The default implementation of that in the plugin is grails.plugin.springsecurity.userdetails.GrailsUser but that's easily customized.
The UserDetails instance is typically a lightweight object, just containing the username and hashed password, a few locked/enabled booleans, and a collection of GrantedAuthority instances to store role names. I often recommend that users extend this to also contain data that's useful but unlikely to change during a login session, e.g. full name, to avoid going to the database to retrieve it. Since the UserDetails is stored in the session and easily accessible via springSecurityService.principal it's a great place to store data like this.
But it is not the same thing as what's returned from getCurrentUser()/currentUser - this is the GORM user/person domain class that was loaded by the UserDetailsService to create the UserDetails instance. It can have a lot more data associated with it, lazy-loaded hasMany collections, etc. It's often a rather large object that should not be stored in the session. Doing so does make its data conveniently available, but will affect scalability because you waste server memory and limit the number of concurrent sessions a server can have. And it's a disconnected Hibernate object, so to use it for most persistence-related actions requires that you reload the instance anyway, often with merge(). That loads the whole thing from the database, so it's a lot more efficient to store the extra data you need in the UserDetails, along with the id of the instance so you can easily retrieve the instance as needed. That's what getCurrentUser()/currentUser does - it uses the id if it's available for a get() call, or the username for the equivalent of a findByUsername() call (which should be around the same cost if the username has a unique index).
After HttpSession inspecting session.getAttributeNames(), i want to share my result :
session.getAttribute('SPRING_SECURITY_CONTEXT').authentication.principal

Caching lookups in MVC, default expiration?

I am caching lookup data in my mvc application, I have the following code:
// GET: Category Types
public JsonResult GetAuditGrants(int auditID)
{
AuditDAL ad = new AuditDAL();
if (System.Web.HttpContext.Current.Cache["AuditGrants"] == null)
{
System.Web.HttpContext.Current.Cache["AuditGrants"] = ad.GetAuditIssueGrants(auditID);
}
var types = (IEnumerable<Grant>)System.Web.HttpContext.Current.Cache["AuditGrants"];
return this.Json(types.ToList());
}
If expiration is not set, by default when does the data expire in cache? Is recommended and should it be stored in the webconfig for consistency for lookup data in my app?
To answer your first question, we can consult MSDN. According to its documentation, adding an object using the Item property (or indexer) is equivalent to calling the Insert method, whose documentation states:
The object added to the cache using this overload of the Insert method
is inserted with no file or cache dependencies, a priority of Default,
a sliding expiration value of NoSlidingExpiration, and an absolute
expiration value of NoAbsoluteExpiration.
Your second question is really pretty application-specific. The best practice is to profile your application. If your application is experiencing a ton of cache-misses and your cache stays small, then you might want to extend the expiration sliding window by using one of the Add or Inserts overloads that give you that control. In that case, storing your selected parameters in the app settings seems like a good idea.
One thing to remember about this cache, however: it is per-app domain. If you have multiple web frontends, or even an IIS server configured to launch more than one worker process for your app, then you may not be getting the most out of your caching strategy. In that case, you might need to use something that can offer persistence to multiple instances of your app. We use Redis, but there are many other options.

Breeze and RESTful WebAPI

Question:
What value does breeze provide when I need to implement my own POST/PUT/GET endpoints per entity in WebAPI?
Background:
This seems to be a common implementation of a serverside Breeze controller:
[BreezeController]
public class TodosController : ApiController {
readonly EFContextProvider<TodosContext> _contextProvider =
new EFContextProvider<TodosContext>();
// ~/breeze/todos/Metadata
[HttpGet]
public string Metadata() {
return _contextProvider.Metadata();
}
// ~/breeze/todos/Todos
// ~/breeze/todos/Todos?$filter=IsArchived eq false&$orderby=CreatedAt
[HttpGet]
public IQueryable<TodoItem> Todos() {
return _contextProvider.Context.Todos;
}
// ~/breeze/todos/SaveChanges
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle) {
return _contextProvider.SaveChanges(saveBundle);
}
// other miscellaneous actions of no interest to us here
}
I'm in the middle of building a RESTish API that, up to this point, has endpoints like:
GET /api/todo/1
PUT /api/todo
POST /api/todo
It seems like Breeze requires the endpoints to be much simpler (for better or worse) - just a bunch of GETS and a SaveChanges POST endpoint.
This leads me to think that Breeze makes rapid development with a single web client, well, a breeze... but as soon as you have anonymous clients, you have to force them into whatever breeze interface conventions you've created in your client, which seems to defeat the purpose of RESTful API design. Is this the case?
Breeze is, first and formost, a client-side JavaScript framework. If you're not using Breeze on the client, the benefits of Breeze.WebApi are limited to
Enhanced OData query support ($select and $expand support, extended $orderby)
Save interception points (beforeSaveEntity and beforeSaveEntities events)
Save result handling (updated entity keys, concurrency columns)
Metadata extraction and serialization
As you've surmised, Breeze has a different philosophy from REST regarding CRUD operations.
Breeze is designed for clients who may want to C/U/D many resources, of different types, in a single transaction. This allows users to manipulate the data in complex ways without hitting the server, then saving their changes when they are ready. For example, one could create a new Order, move two OrderLineItems from one Order to another, delete a third OrderLineItem, modify the quantity on a fourth, and then SaveChanges(). Breeze even supports using localStorage to work completely disconnected from the server. Once reconnected, the changes can all be saved.
REST was designed to operate on one resource at a time. Each C/U/D operation must be performed against the server immediately so that the response code can be acted upon. It works well for applications with simple update needs, but not for data-entry applications. While transactions can be supported in REST, they are cumbersome at best.
Having said that, your server-side Breeze API is not limited to what you see in the Todos example. Breeze supports Named Saves, which allows you to have different endpoints for different operations. You can also use Save Interception to ensure that your save bundle only contains the types that it should. And naturally, there's nothing preventing you from exposing both APIs on your server, and having both fed by the same persistence layer.
If you have to decide between them, you should start with your users. Real users (not developers) don't care about REST, they care about what the application can do. Ultimately, REST gives your application all the semantics of HTTP, and Breeze gives it all the semantics of a relational or object database. Which one to expose to your users should depend upon the use cases you need to support.

Resources