.net core controller action sharing values using HttpContext - asp.net-core

I've two controller action in my application , FirstAction and SecondAction.
When the FirstAction is called , I want to store a data and want to reuse in SecondAction.
I've tried HttpContext and cannot get value in SecondAction. It always retrun null .
public async Task<IActionResult> FirstAction()
{
HttpContext.Items.Add("Key1", myValue1);
HttpContext.Items.Add("Key2", myValue2);
}
public async Task<IActionResult> SecondAction()
{
var _value1 = HttpContext.Items["Key1"]?.ToString();
var _value2 = HttpContext.Items["Key2"]?.ToString();
}
Is there any better way for such kind of scenairo ?

HttpContext.Items stores data for a single request only. From the docs on state management :
The HttpContext.Items collection is used to store data while processing a single request. The collection's contents are discarded after a request is processed.
You can store data in session state to preserve it from one request to the next, as long as a user's session is active. You'd have to configure session storage first. The in-memory option is only valid for a single server. Database or Redis storage are suitable for server farms used eg in load balancing scenarios.
The docs show how to set and read session state using strongly typed methods, eg :
HttpContext.Session.SetString(SessionKeyName, "The Doctor");
HttpContext.Session.SetInt32(SessionKeyAge, 773);
and
var name = HttpContext.Session.GetString(SessionKeyName);
var age = HttpContext.Session.GetInt32(SessionKeyAge);

Related

Is there a Session-unique identifier that can be used as a cache key name?

I'm porting a legacy ASP.NET WebForms app to Razor. It had stored an object in the Session collection. Session storage is now limited to byte[] or string. One technique is to serialize objects and store as a string, but there are caveats. Another article suggested using one of the alternative caching options, so I'm trying to use MemoryCache.
For this to work as a Session replacement, I need a key name that's unique to the user and their session.
I thought I'd use Session.Id for this, like so:
ObjectCache _cache = System.Runtime.Caching.MemoryCache.Default;
string _keyName = HttpContext.Session.Id + "$searchResults";
//(PROBLEM: Session.Id changes per refresh)
//hit a database for set of un-paged results
List<Foo> results = GetSearchResults(query);
if (results.Count > 0)
{
//add to cache
_cache.Set(_keyName, results, DateTimeOffset.Now.AddMinutes(20));
BindResults();
}
//Called from multiple places, wish to use cached copy of results
private void BindResults()
{
CacheItem cacheItem = _cache.GetCacheItem(_keyName);
if (cacheItem != null) //in cache
{
List<Foo> results = (List<Foo>)cacheItem.Value;
DrawResults(results);
}
}
...but when testing, I see any browser refresh, or page link click, generates a new Session.Id. That's problematic.
Is there another built-in property somewhere I can use to identify the user's session and use for this key name purpose? One that will stay static through browser refreshes and clicks within the web app?
Thanks!
The answer Yiyi You linked to explains it -- the Session.Id won't be static until you first put something into the Session collection. Like so:
HttpContext.Session.Set("Foo", new byte[] { 1, 2, 3, 4, 5 });
_keyName = HttpContext.Session.Id + "_searchResults";
ASP.NET Core: Session Id Always Changes

Managing CosmosDb Session Consistency levels with Session Token in web environment

My environment is ASP.NET Core 2.x accessing CosmosDb (aka DocumentDb) with the .NET SDK.
The default consistency level of my collection is set to "Session". For my use-case I need a single authenticated web user to always have consistent data in terms of reads/writes between web requests.
I have some CosmosDB Repository logic that is made available to my controller logic via ASP.NET Core Singleton dependency injection as such:
services.AddSingleton<DocumentDBRepository, DocumentDBRepository>(x =>
new DocumentDBRepository(
WebUtil.GetMachineConfig("DOCDB_ENDPOINT", Configuration),
WebUtil.GetMachineConfig("DOCDB_KEY", Configuration),
WebUtil.GetMachineConfig("DOCDB_DB", Configuration),
"MyCollection",
maxDocDbCons));
DocumentDBRespository creates a cosmos client like so:
public DocumentDBRepository(string endpoint, string authkey, string database, string collection, int maxConnections)
{
_Collection = collection;
_DatabaseId = database;
_Client = new DocumentClient(new Uri(endpoint), authkey,
new ConnectionPolicy()
{
MaxConnectionLimit = maxConnections,
ConnectionMode = ConnectionMode.Direct,
ConnectionProtocol = Protocol.Tcp,
RetryOptions = new RetryOptions()
{
MaxRetryAttemptsOnThrottledRequests = 10
}
});
_Client.OpenAsync().Wait();
CreateDatabaseIfNotExistsAsync().Wait();
CreateCollectionIfNotExistsAsync().Wait();
}
As far as I understand that means one CosmosDB client per Web App server. I do have multiple web app servers, so a single user might hit the CosmosDB from multiple AppServers and different CosmosDb clients.
Before a user interacts with the ComosDB, I check their session object for a CosmosDb SessionToken, like so:
string docDbSessionToken = HttpContext.Session.GetString("StorageSessionToken");
Then, when writing a document for example, the method looks something like so:
public async Task<Document> CreateItemAsync<T>(T item, Ref<string> sessionTokenOut, string sessionTokenIn = null)
{
ResourceResponse<Document> response = null;
if (string.IsNullOrEmpty(sessionTokenIn))
{
response = await _Client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(_DatabaseId, _Collection), item);
}
else
{
response = await _Client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(_DatabaseId, _Collection), item, new RequestOptions() { SessionToken = sessionTokenIn });
}
sessionTokenOut.Value = response.SessionToken;
Document created = response.Resource;
return created;
}
The idea being that if we have a session token, we pass one in and use it. If we don't have one, just create the document and then return the newly created session token back to the caller. This works fine...
Except, I'm unclear as to why when I do pass in a session token, I get a DIFFERENT session token back. In other words, when _Client.CreateDocumentAsync returns, response.SessionToken is always different from parameter sessionTokenIn.
Does that mean I should be using the new session token from that point on for that user? Does it mean I should ignore the new session token and use the initial session token?
How long do one of these "sessions" even last? Are they sessions in the traditional sense?
Ultimately, I just need to make sure that the same user can always read their writes, regardless of which AppServer they connect with or how many other users are currently using the DB.
I guess the confusion here is on what a session is?
In most scenarios/frameworks treat session as static identifier (correlation), where as with cosmos the sessionToken is dynamic (kind of bookmark/representation of cosmos db state, which changes with writes). Naming it as 'sessionToken' might be root of the confusion.
In this specific scenario, you should use the "returned sessiontoken" from cosmos API's.

ServiceStack: Can we Pass Data through a RequestFilterAttribute to the calling service

Maybe I'm thinking about this wrong, but I'm trying to create a custom attribute for our CMS to handle auth checks.
https://gist.github.com/sitefinitysteve/62ab761256a64a84d8a6#file-sitefinityjwt-cs-L39
So if this service is called from within the CMS from a logged in user, user data is all there for the service method already.
But in the context of being called from an app, the user is technically Anonymous, however I can decode the token and get the user just fine... but not sure how to like pass that over to the service.
Am I just maybe looking at this wrong, and the proper thing to do is to call a CMS API method to just log that person in (seems slow if I already have the persons user object from line 33, and the service context expires instantly.
Use Request.Items Dictionary
You would use the IRequest.Items dictionary for any data you want to pass throughout ServiceStack's Request Pipeline:
//RequestFilter:
req.Items["info"] = new MyRequestInfo { ... };
In Service:
var info = (MyRequestInfo)base.Request.Items["info"];
Have DTO's share common interface
Another option for adding extra info to your Service is to have Request DTO's implement an interfaces, e.g:
public interface IHasInfo
{
MyRequestInfo Info { get; set; }
}
Which you could then populate in your Request Filter, e.g:
((MyRequestInfo)dto).Info = new MyRequestInfo { ... };
Access in Service like any other DTO property, e.g:
public object Any(Request request)
{
var info = request.Info;
}

How can I update user data form session in ServiceStack?

Very simple question: I have a session object in my Service:
var session = this.GetSession(); //IAuthSession
if (!session.IsAuthenticated)
I can modify some values in the session class (e.g. Permissions) based on the parameters passed to the service; then I want to save them.
How?
The direct way of doing it: create a UserAuth object, popolate it with all the fields from IAuthSession, get the IDbConnectionFactory, save it.
Surely there is a faster and better way, but I was not able to find it!
More generally, how can I switch between IAuthSession anf UserAuth? I.e., given a IAuthSession object, how can I obtain a UserAuth object, modify it, and persist the modifications?
I have read this question on how to append metadata to a user login info, but something is still missing.
Once you have added what you need, how do you save it? (I doubt you just add the metadata to both session and UserAuth, and then you use IDbConnectionFactory to save the latter; there must be a better way!)
Old question but worth answering.
The UserAuthRepository being used should have an implementation of the UpdateUserAuth method that can be called to save the UserAuth changes
UpdateUserAuth(UserAuth existingUser, UserAuth newUser, string password)
Another easier way would be to just call the RegisterService using PUT which will update the existing registered user for you.
/// <summary>
/// Update an existing registraiton
/// </summary>
public object Put(Register request)
{
return Post(request);
}
The service call would be something similar to this:
using (var authService = base.ResolveService<RegisterService>())
{
var authResponse = authService.Put(
new Register {
UserName = session.UserName ?? session.Email,
Email = session.Email,
etc...
});
if (authResponse is IHttpError)
throw (Exception)authResponse;
}

How to store info about the authenticated user in WCF?

I have a WCF service where I use a customUserNamePasswordValidatorType (specified in the behaviors\serviceBehaviors\serviceCredentials\userNameAuthentication section of the web.config file).
My custom UserNamePasswordValidator works that way:
public bool Authenticate(string userName, string password)
{
If ( IsUserValid(username, password) )
{
UserInfo currentUser = CreateUserInfo(username);
//
// Here I'd like to store the currentUser object somewhere so that
// it can be used during the service method execution
//
return true;
}
return false;
}
During the service call execution, I need to access the info of the authenticated user. For instance I would like to be able to implement:
public class MyService : IService
{
public string Service1()
{
//
// Here I'd like to retrieve the currentUser object and use it
//
return "Hello" + currentUser.Name;
}
}
My question is how and where should I store the information during the authentication process so that it can be accessed during the call execution process? That storage should only last as long as the "session" is valid.
By the way, I don't use (and don't want to use) secure sessions and/or reliable sessions. So I have both establishSecuritytContext and reliableSessions turned off.
I'm thinking of enabling ASP.NET Compatibility Mode to store the user info in the HttpContext.Current.Session but I have the feeling it's not how it should be done.
Store anything that needs to be persisted into a persistant store - e.g. a database, that's the best way to go.
Store the user info in a user table, e.g. the ASP.NET membership system or something of your own. Keep some kind of a identifying token (username, ID etc.) at hand to retrieve that info from the database when needed.
You should strive to have a stateless WCF service whenever possible - it should never depend on a "state" of any kind other than what's safely stored in a database.