ASP.NET MVC: How to handle cross-action TempData and ViewData - c#

I'm trying to find a good way to handle the following scenario (I'm still kinda new to this):
A user can register through my site using an RPX/OpenId provider.
Step 1: The user authenticates through a provider. The provider returns a temporary token to one of my action methods.
Step 2: I use the token to grab the user's profile information and load a view which allows them to enter any missing required fields and optional fields.
I use 2 action methods in Step 2: One to handle the grabbing of information using the token. A second action which takes the authorization information and loads the missing/optional fields view.
I am passing the authorization info via TempData to the second action. The second action can handle validation so there's a chance I will need to hold on to the authorization object for more than just 1 request. I can't use the token to regenerate the authorization info because it's technically a one-use token, and it would be silly to regenerate the request since it is using network resources.
How could I persist the objects in my TempData for any subsequent requests to the same action, but remove the objects for any redirects? And since this may be a repeatable pattern in my application should I create a Filter to automatically handle this situation?
For example, I imagine a filter attribute which will merge TempData (if any) into ViewData - But how would I persist my data into future calls to the same action? Throw it into TempData again? And if I detect a redirect empty the TempData?
Thanks

I ended up re-adding the data to TempData if necessary. Since TempData does not allow you to add duplicate keys I created my own helper method to remove and then re-add the key:
public static void AddNew(this TempDataDictionary tempData, string key, object obj)
{
if ( tempData.ContainsKey( key ) ) tempData.Remove( key );
tempData.Add( key, obj );
}

I've had the same problem, but approached it a slightly different way - I've used a Forms Authentication method which works solely with OpenID... if my provider returns Authenticated, I do the following
var fields = openid.Response.GetExtension(typeof(ClaimsResponse)) as ClaimsResponse;
if (fields != null)
{
TempData["Email"] = fields.Email;
TempData["Nickname"] = fields.Nickname;
}
FormsAuthentication.RedirectFromLoginPage(openid.Response.ClaimedIdentifier, false);
break;
This means that I don't need to pass any kind of Authentication token around - after this code executes I know my user is authenticated.
Therefore, the action that this passes control to will simply copy the TempData fields, if populated, into ViewData and pass them onto the view.
Validation is handled after this - I don't care what comes back from OpenID (i.e. is it valid or not), I let the user edit this and then save, and then perform my validation.

Related

ASP.NET MVC security when calling an action directly

I have a controller called Account and an action called AddFunds()
In my HttpGet Addfunds() action I return the view bind to a ViewModel with a AccountId property, so by default the view contains a hidden field called AccountId.
In the HttpPost AddFunds() action, I received the updated ViewModel, with the same AccountID as passed by the HttpGet method, with some other parameters, such as Amount, etc.
What can I do to prevent a person to invoke the method directly passing a fake AccountId?
Does the AntiForgery token prevent this?
Any other measure should I take?
As a side question, does passing the AccountID in a hidden field can be avoided or is it necessary to know which entity I am acting upon?
Thanks
First thing - you should really avoid using hidden fields for data/IDs, which are about to be passed to the controller's action and represent some real and possibly important data.
If you are concerned about user, who is given the possibility to manipulate ID you should either introduce not deterministic ID(like GUID).
Anti-forgery token is used to prevent performing CSRF attack.
You can encrypt the Id when you passing the value to post action and decrypt that id in the post method wherever it required.
your url will be : localhost:3040/home/edit?AccountId=hkdshjlk89890-32(encryptedid)
Or you can use base64 encode and decode(I won't recommend base64 because as every one decode the value).
Other solution you can see the below link
Only allow access to action if redirected from specific action

Prevent caching of layout file in MVC when caching Index action method

I have many create Views in my MVC application. I have cached the GET Index action method using [OutputCache] annotation, so that the view is stored in the cache.
I store the details about the currently logged in user in the Session, and display the user's first name in the layout after reading it from the Session.
The problem is, since I have cached the views, the layout is also cached. So even if a different user logs in, the first name of the previous user is visible, because the layout was cached last time.
Is there any way to prevent caching of the layout? Or is there any other way I can stop the first name display from getting cached?
I thought about using VaryByCustom, but I am not sure what to do in the GetVaryByCustomString method that I will need to override.
What would be the best approach to prevent caching of the layout, or alternately, varying the cache by user?
EDIT:
I must clarify that I am using my own custom user management logic. I have my own Users table in the database and I retrieve relevant data on login and store it in Session.
Here's one way you could use the GetVaryByCustom function in Global.asax.
public override string GetVaryByCustomString(HttpContext context, string custom)
{
var varyString = string.Empty;
if(context.User.Identity.IsAuthenticated)
{
varyString += context.User.Identity.Name;
}
return varyString;
}
What you could also do is get the user information with an ajax request and insert it using javascript, that way you don't need to recache the page for each user. Personally I would go with this approach if the username is the only thing that differs.

Bypass server-side model validation dynamically ASP.NET MVC

I am now working on an existing asp.net MVC application which has a few submit button in one web page(e.g. ignore, delete, update, submit...) And they all send a post request to the server and the server will perform different operation based on the request action type (e.g. ignore, delete, update, submit).
However, an issue is raised that the model validation will always triggered regardless of the operation. According to the business rule, we only need to perform validation for some certain operations. I can clear the error in the modelstate after the validation. (for example MVC3 Remove ModelState Errors).
However I am hoping there is a better way to just bypass the model validation according to the operation parameter from the client.
BTW: I am not allowed to significantly change the way how it works (e.g. send to another action for those operation that does not care about the validation), so I am hoping there is some trick that I can use to achieve this easily.
I assume now you are checking model state errors like...
if (ModelState.Valid)
{... valid and do something...}
If so, you can include some additional checking before hand that will be considered in your conditional. For instance, if it is a delete submit, you can check that before hand.
bool isDelete = ***check some value***
if (isDelete || ModelState.Valid)
{... valid and do something...}
This way even if the model state is invalid, it will still move forward.
One thing to consider is you will need to set something in the model to tell you what action is happening. This probably means using javascript to capture the submit, set a model field, and then perform the submit.

Passing model from a post method to a new get method in MVC

I have an application.
I am stuck at a point from where i want to pass a model from a Post method to a Get method.
// Code in get method
FoundAccounts fa=new FoundAccounts();
fa.register = model;
return RedirectToAction("FoundAccounts", fa);
//Post Method
public ActionResult FoundAccounts(FoundAccounts fa)
{
//Use that values here
}
Can i do it like this?
I am unable to find a way.
Please help me with the same.
Thanks
Can i do it like this?
No, you can't. You can only pass simple, scalar properties to the route values of a RedirectToAction call. It doesn't make sense to pass complex objects, because when you perform a redirect, only the simple properties will be included in the GET request as query string parameters.
So you have different possibilities:
Persist the entity in your backend and then pass only the id to the GET action (this is the solution I recommend):
int id = repository.Save(fa);
return RedirectToAction("FoundAccounts", new { id = id });
and then your GET action will take the id as action parameter and use this id to retrieve the entity from wherever you persisted it initially:
public ActionResult FoundAccounts(int id)
{
FoundAccounts model = repository.Get(id);
...
}
Pass all properties and leave the model binder dehydrate the entity in the GET action:
return RedirectToAction("FoundAccounts", new
{
prop1 = fa.prop1,
prop2 = fa.prop2,
...
});
Obviously here if you have some complex properties you will need to pass them as well. Remember that the properties you include will be the properties you will be able to retrieve in your GET action. Everything else will be lost:
return RedirectToAction("FoundAccounts", new RouteValueDictionary
{
{ "prop1.SubComplexProp1", fa.prop1.SubComplexProp1 },
{ "prop1.SubComplexProp2", fa.prop1.SubComplexProp2 },
{ "prop2", fa.prop2 },
});
The drawback of this solution is that if you have lots of properties this could quickly become cumbersome. And you could even hit on a roadblock because there's a limitation to the size of a GET request. This limitation will vary between browsers, but I wouldn't pass anything more than 2048 characters in a GET request.
Use Session or TempData (not recommended as it introduces state into your application);
TempData["fa"] = fa;
return RedirectToAction("FoundAccounts");
and then inside the GET action retrieve the model from the Session or TempData:
public ActionResult FoundAccounts()
{
FoundAccounts model = TempData["fa"] as FoundAccounts;
...
}
The difference between Session and TempData is that TempData will survive only for a single redirect and will then be automatically evicted by the framework. Under the covers it uses Session, it's just that it is automatically cleared once you read the value in the GET action. The problem with this of course is that if the user hits F5 to refresh the page in his browser, you will no longer find the value in TempData because it was evicted. Even worse if the user decides to bookmark the GET action, he will have the same problem if later he decides to come back and navigate to this bookmark. So people tend to use Sessions for those kind of things. Sessions of course do not solve the problem with bookmarks (because the user could have closed his browser in-between and the Session will be lost). Also Sessions introduce other problems in web farm scenarios. For example if the session is stored in-memory (which is the default), this means that you could store the value on one node of the farm but when you perform the redirect you could hit another node of the farm and then this node no longer has any knowledge of the session. So people start to use an out-of-process sessions - either stored in a State Service machine or SQL service. But what's the point? I mean take a look at my first and recommended solution. It's exactly what you will end up a fortiori if you want to have scalable solution working in a web farm environments.

Global Variable inside of Controller MVC

So, I have this variable that I push into the controller via POST from a form in my view.
I then push the variable into viewdata so it's available to the next view which is fine. But that view has no need of a form so I'm unable to push that same variable into the next controller. In short, it's cumbersome to push pieces of information back and forth from controller to view and reverse, so I'm looking for a way to keep a global variable alive inside a controller so that it's accessible by all action results... The general breakdown of my program is this...
-User types a "name"
-I send "name" to controller.
-I push 'name' into viewstate (query entity framework to get a list of stuff 'name'
has access to) and return that list into the view.
-In that view I can access the 'name' since it was in view state.
-Clicking on a link inside the page takes me to another controller where I need
to get access to 'name' WITHOUT passing view Routing or POST.
Obviously the easiest way would be to declare 'name' globally and then it's always available but for the life of me I can't figure out how.
Have you considered storing it in the Session?
This will allow you to easily access it, either from your controller or views, and avoids the need for global variables.
Storing:
[HttpPost]
public ActionResult YourPostMethod(string name)
{
Session["Name"] = "yourName";
}
Access: *
Make Sure to check that it exists prior to grabbing it:
var whatsMyName = (Session["Name"] != null) ? Session["Name"] : "";
Scalability Consideration
It's worth mentioning that MVC applications are designed to mimic the web and are stateless. Introducing Session variables changes this, so be aware that it can introduce issues regarding scalability, etc.
Each user that stores data within the Session will take up resources at the server level. Depending on the number of users and what you are storing within the Session, you could potentially run out of memory if those values become too large.
Why not use the session object, which is an associative array which lives while the client is connected?
$_SESSION['name'] = "zzzz"; // store session data
name = $_SESSION['name']; //retrieve data
You can use this for each user till their session is active.. hope this helps

Categories