The best way to save the String Variable after being redirected Page in ASP.NET CORE - asp.net-core

How can I save the form values as a string after the page is redirected?
I use a static variable, if the number of users on the site increases for example 20000, will the site have problems?
Which method is better?
Using a static variable?
session?
Cookies?
ViewBag?
View State?
sql server database?

Actually, There is no the best method, Only the most suitable method. The methods you list above all have their own most suitable use cases.
From your comment, I think you may wanna redirect to another page with a string parameter, So you can try to use :
return RedirectToAction("actionName","controllerName",new { Name="xxx"});
It will send a get request: acontrollerName/actionName?Name=xxx.
Or, if you wanna save that string value for a long time, You can also try to use Session or Database. But one thing you need to consider is that you need to set the session time out according to the actual situation of your project.
==========================edited===========================
I think you can use Session to achieve it.
public class TestModel
{
public int ID { get; set; }
public string Name { get; set; }
}
public async Task<IActionResult> OnPostSelectMenu(int selectedid)
{
TestModel selected = await _db.Table.FindAsync(selectedid);
//set session
HttpContext.Session.SetString("Key", JsonConvert.SerializeObject(selected));
return RedirectToPage("MyPage");
}
public async Task<IActionResult> OnGetAsync()
{
//......
//get session
Input = JsonConvert.DeserializeObject<TestModel>(HttpContext.Session.GetString("Key"));
return Page();
}

Related

Open touch\face ID\biometric auth in blazor wasm app

I need to add the biometric authentication to my blazor wasm app.
I've found some hard to implement\understand libraries that uses a third part authentication server side.
I only need to open the biometric's dialog from the device (iOS, Android, desktop browser..etc..) then I would to manage manually the authentication with my actual method.
It is possible? If yes, how?
Thanks! :)
Haven't found anything built into blazor yet, so it seems for now you'll have to use jsinterop. Haven't actually tried it in Blazor, but I'd expect it to happen mostly in the js code anyway. There's a nice website just for this, since you have to add a few options and figure out asymmetric keys: https://webauthn.guide/
Might try it soon to find out if there's a more compressed way of presenting an MCVE - if I do figure it out I will update this.
Update
Tried it, this opens the dialog for me.
The actual call happens in JS:
async function doIt(options) {
var newCreds = await navigator.credentials.create({ publicKey: options });
console.log("Created new credentials:", newCreds);
return newCreds;
}
window.doIt = doit;
Put a button into my razor:
<button #onclick="DoIt">Do it</button>
Then the corresponding method and types in the #code block:
private async void DoIt()
{
var credOptions = new PublicKeyCredentialCreationOptions();
Console.WriteLine("Sending options for " + credOptions.user.displayName);
var cred = await Js.InvokeAsync<PublicKeyCredential>("doIt", credOptions);
Console.WriteLine("Received cred");
Console.WriteLine(cred);
}
// Do all the things (I think this is like pattern/faceID/touchID/etc)
static readonly int[] Algs = { -7, -8, -35, -36, -37, -38, -39, -257, -258, -259, -65535 };
private static PublicKeyCredentialCreationOptions.PublicKeyCredentialParameters Alg(int alg)
{
return new PublicKeyCredentialCreationOptions.PublicKeyCredentialParameters("public-key", alg);
}
public class PublicKeyCredentialCreationOptions
{
public byte[] challenge { get; set; } = DateTime.Now.ToString().Select(c => (byte)c).ToArray(); // Just random stuff here I think?
public RelyingParty rp { get; set; } = new ("WebAuthnExample"); // If I understand correctly, id will be auto filled with the current domain
public User user { get; set; } =
new("metallkiller".Select(c => (byte)c).ToArray(), "metallkiller", "Metallkiller");
public PublicKeyCredentialParameters[] pubKeyCredParams { get; set; } = Algs.Select(Alg).ToArray();
public long timeout { get; set; } = 60000; // Not entirely sure what this does - docs has more info
public string attestation { get; set; } = "direct"; // No idea I just copied this
public record RelyingParty(string name);
public record User(byte[] id, string name, string displayName);
public record PublicKeyCredentialParameters(string type, int alg);
}
Good news: This opens the biometric auth dialog and creates a credential.
Bad news: Can't get the credential back to dotnet, maybe I forgot to do some jsinterop-magic or it just doesn't work with credentials, then we might have to read all the things in JS and return them in our own object or something. I'd appreciate anyone telling me what's going on here.
Edit: Source for the return types: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/e57ff15825e1a05c923f80f39dbb7966d20db950/types/webappsec-credential-management/index.d.ts

.NET 5 Web API: Storing data per request

When getting a request in any action of any controller, I look at the jwt know which user is requesting and lookup the user in the database to get some user-data that I want to use throughout the application. E.g. which departments the user belongs to or the users preferred language.
Now I could create a object which wraps these information and send it down the layers and pass it to every method that likes to use some of this data. But I like the data to be available to every method throughout the application without passing it in every method. Like e.g. dependency injection (Seems to late at that point) or something else I can get access to that data quickly.
Any advice of how to handle it?
Try it with the Items property on the HttpContext. By using it you can store data during a single request. The only downside with this approach is that every service needs to have access to the HttpContext to read the values. Values can be added to the Items Dictionary as shown below
public class IndexModel : PageModel
{
//
public void OnGet()
{
HttpContext.Items.Add("Key", new RequestInfo { Key = "RequestKey" });
}
}
class RequestInfo
{
public string Key { get; set; }
}
You can then access the value by registering the IHttpContextAccessor to the IServiceCollection and then using Constructor injection to use the HttpContext in your service so that you can work with the Items Dictionary.
public class Service
{
private IHttpContextAccessor _htp;
public Service(IHttpContextAccessor http)
{
_htp = http;
}
public void Log()
{
Console.WriteLine(((RequestInfo)_htp.HttpContext.Items["Key"]).Key);
}
}

Asp .Net MVC on action executing - Get the value of action parameter values of user defined types on action executing

I want to log the each action method parameter name and its
corresponding values in the database as key value pair. As part of
this, I am using OnActionExecuting ActionFilterAttribute, since it
will be the right place (OnActionExecuting method will get invoke for
all controller action methods call) to get Action Executing context.
I am getting the value for .Net types (string, int, bool). But I am
unable to get the value of the User defined types (custom types).
(ex: Login model). My model might have some other nested user
defined types as well.
I was trying to get the values of the user defined types but I am
getting the only class name as string. I hope we can do in
reflection.
Could you please anyone assist to resolve the issue. since I am new
to reflection. It will helpful to me. Thanks in Advance.
I need to get the name and value of these types in OnActionExecuting.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
ActionParameter = new SerializableDictionary<string,string>();
if(filterContext.ActionParameter != null)
{
foreach(var paramter in filterContext.ActionParameter)
{
//able to get returnUrl value
//unable to get model values
ActionParameter.Add(paramter.Key, paramter.Value);
}
}
}
public ActionResult Login(LoginModel model, string returnUrl)
{
return View(model);
}
User defined type
public class LoginModel
{
public string UserName {get;set;}
public string Password {get;set;}
//User defined type
public UserRequestBase Request {get;set;}
}
//User defined type
public class UserRequestBase
{
public string ApplicationName {get;set;}
}
I am able to get the value of the returnUrl (login method param) in OnActionExecuting but not for model (login method param). I am able to see the values, but don't know how to access it, I used typeof even though I am unable to get it, but I need generic because i have 20 methods in controller so I could not only for LoginModel.
This answer isn't exactly what you want - based on your question - but I think it will work better for what want to accomplish. Quick aside...
Playing around with reflection and nested classes in this instance, lead to some SO (a propos?) errors for me...
So, a better path, maybe? Rather than trying to get/cast the property names, values (types?) from 'context.ActionParameters,` I found it was much easier to let a Json serialization do the work for me. You can then persist the Json object, then deserialize... pretty easy.
Anyway, here's the code:
using Newtonsoft.Json; // <-- or some other serialization entity
//...
public class LogActions : ActionFilterAttribute, IActionFilter
{
// Using the example -- LoginModel, UserRequestBase objects and Login controller...
void IActionFilter.OnActionExecuting(ActionExecutingContext context)
{
var param = (Dictionary<String, Object>)context.ActionParameters;
foreach (var item in param.Values)
{
string itemName = item.GetType().Name.ToString();
string itemToJson = JsonConvert.SerializeObject(item);
// Save JsonObject along with whatever other values you need (route, etc)
}
}
}
Then when you retrieve the Json object from the database you just have to deserialize / cast it.
LoginModel model = (LoginModel)JsonConvert.DeserializeObject(itemToJson, typeof(LoginModel));
From example:
public class LoginModel
{
public string UserName {get;set;}
public string Password {get;set;}
//User defined type
public UserRequestBase Request {get;set;}
}
//User defined type
public class UserRequestBase
{
public string ApplicationName {get;set;}
}
Controller used in example:
public ActionResult Login(LoginModel model, string returnUrl)
{
return View(model);
}
Hope this helps. If there are further issues with this please let me know and I will try to help.

site wide variables per user

I have an MVC4 site that needs to maintain some information while (and ONLY while) the user is logged in. For example, once the user logs in, I get a 'user token' back that allows me access to several off site services.
I've tried two different approaches. The first was to use a public static class that accesses the user session. However, after reading up on static classes, I'm hesitant to use them. According to what I'm reading, static classes should only be used for read only objects, and I wasn't using it that way. Although the site site did seem to be working fine with a limited number of users (currently there's 10).
(If someone would like to explain to me why this is a bad idea in MVC4, please tell me and/or link to an article)
public class SessionAccessorClasses
{
public const string SessionAccessorSessionVariablesString = "_SessionAccessorSessionVariables";
public static SessionAccessorModel SessionVariables
{
get { return System.Web.HttpContext.Current.Session != null ? (SessionAccessorModel)System.Web.HttpContext.Current.Session[SessionAccessorSessionVariablesString] : null; }
set { System.Web.HttpContext.Current.Session.Add(SessionAccessorSessionVariablesString, value); }
}
}
My second (and current) approach is to use Session variables and access them using a globally available class.
public class SessionAccessorClasses
{
private const string SessionAccessorSessionVariablesString = "_SessionAccessorSessionVariables";
public SessionAccessorModel GetSessionVariables()
{
return System.Web.HttpContext.Current.Session != null ? (SessionAccessorModel)System.Web.HttpContext.Current.Session[SessionAccessorSessionVariablesString] : null;
}
public void SetSessionVariables(SessionAccessorModel value)
{
System.Web.HttpContext.Current.Session.Add(SessionAccessorSessionVariablesString, value);
}
public void ClearSessionVariables()
{
System.Web.HttpContext.Current.Session.Remove(SessionAccessorSessionVariablesString);
}
}
This works fine, but I hesitate to call it good is because I don't fully understand why the public static class was such a bad idea, and because I now have to instantiate my new class at the beginning of nearly every function, and call the Set/Update function at the end of every function; which feels wrong somehow.
So first, since my original static class was accessing the users session, is it really that bad?
Second, is my second class a more appropriate way of doing things? Can you suggest improvements?
Third, if nothing else, can you give me the positive/negative aspects of doing it either way?
You want to use Session in ASP.net. It was created for the purpose you describe.
ASP.NET session state enables you to store and retrieve values for a user as the user navigates ASP.NET pages in a Web application. HTTP is a stateless protocol. This means that a Web server treats each HTTP request for a page as an independent request. The server retains no knowledge of variable values that were used during previous requests. ASP.NET session state identifies requests from the same browser during a limited time window as a session, and provides a way to persist variable values for the duration of that session. By default, ASP.NET session state is enabled for all ASP.NET applications.
I'm a fan of strongly-typed reusable session variables, so I wrote the following extensions to store whatever variables you want to create without the need to constantly remember magic strings.
public static class SessionExtensions
{
public static bool TryGetValue<T>(this HttpSessionStateBase session, out T value)
where T : class
{
var name = typeof(T).FullName;
value = session[name] as T;
var result = value != null;
return result;
}
public static void SetValue<T>(this HttpSessionStateBase session, T value)
{
var name = typeof(T).FullName;
session[name] = value;
}
public static void RemoveValue<T>(this HttpSessionStateBase session)
{
var name = typeof(T).FullName;
session[name] = null;
}
public static bool ValueExists(this HttpSessionStateBase session, Type objectType)
{
var name = objectType.FullName;
var result = session[name] != null;
return result;
}
}
So if you have a class:
public MyClass
{
public int MyInt { get; set; }
}
You can store it by simply:
Session.SetValue(MyClass);
that needs to maintain some information while (and ONLY while) the user is logged in.
These methods could be updated a few ways to fulfill this requirement. Here is one way:
public static bool TryGetAuthenticatedValue<T>(this HttpSessionStateBase session,
out T value)
where T : class
{
value = null;
if (HttpContext.Current.User != null
&& HttpContext.Current.User.Identity != null
&& HttpContext.Current.User.IsAuthenticated)
{
var name = typeof(T).FullName;
value = session[name] as T;
}
var result = value != null;
return result;
}
I would also recommend that whatever classes you store in session, be serializable. That is to say it has a parameterless constructor and marked as [Serializable].

Gathering Active Directory Data for MVC intranet application

I need assistance with gathering Active Directory data based on a table in my DB. I have an entity class that holds user requests. Each request has the user's windows name from System.Web.HttpContext.Current.User.Identity.Name. My problem is I cannot figure out how to setup a linq query to associate the AD username to the rest of the AD so I can display their full names instead of their username in my table. Here is what I have so far, any help will be appreciated.
public partial class RequestInfo
{
public int RequestInfoId { get; set; }
public string RequestByUserADId { get; set; }
public System.DateTime RequestDateTime { get; set; }
public string Explanation { get; set; }
public virtual UserInfo UserInfo { get; set; } // where I define my custom roles
}
I can query AD by using the code below. I have tried Get Active Directory User Information With Windows Authentication in MVC 4, but it did not help.
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
using (UserPrincipal user = UserPrincipal.FindByIdentity(context, requestByAdId))
{
return user.DisplayName
}
I may be off here because I am not sure if you are able to successful establish a user principal or not but if you have the user principal you can get property information like the following:
user.GetProperty("propertyName")
Here is a static method that should get you the department for a user, for example.
public static String GetDepartment(UserPrincipal principal)
{
return principal.GetProperty("department");
}
Let me know where this gets you and I can elaborate further if this isn't working.
Edit
It appears you need to go one level deeper to get the fields that aren't by default a part of the user principal. For this you will need to get the directory entry from the user principal first:
DirectoryEntry directoryEntry = (userPrincipal.GetUnderlyingObject() as DirectoryEntry);
Then you need to check if the attribute you are looking for exists, and if it does, get the value. A great way to do this is to create a helper method that you pass your directory entry to along with the string value for the property name that you want to get.
public string GetProperty(DirectoryEntry directoryEntry, string propertyName)
{
if (directoryEntry.Properties.Contains(propertyName))
{
return directoryEntry.Properties[propertyName][0].ToString();
}
else
{
return string.Empty;
}
}
Please note that going to the underlying object is expensive. I believe this operation, by default, is cached for you so subsequent use of this information can be retrieved from cache. Playing around with
directoryEntry.RefreshCache
will get you started with that.
Let me know if this does the trick for you!