I am just starting MVC and I would like to know the best practice to pass sensitive information like IDs across views ...
Let's assume that I have a scenario.
I have a car service managing MVC application which allow users to choose product for their registered car.
The user have to register their car first before they choose a product for their service.
In register view, they fill out the car detail and it redirects to purchase product page when they click the submit button. At the time when they click the submit button, we store car details with user ID (which I can get from Identity) and generate unique car ID from the database. I want to pass this newly created car ID to next view.
In purchase product page, they can choose different product A or B and when they choose, it redirects to checkout page.
What I want to achieve now is then in checkout page, how securely we can carry the car ID that user get after they have registered their car and product ID from previous product view so I can process transaction with userID, carID, and productID.
Is Session way to go with this ? Or any other better way to tackle this problem .?
Someone with small example will be great help for me.
Thanks,
In your example given I would certainly recommend storing the ID in a session. The web is a stateless beast, and what you're essentially after doing is recording state for the duration of the user's visit to the website/application - this is essentially what sessions are designed to do.
Creating, storing and retrieving data from a session is simple and can be done like so:
Setting a variable in the session object
[HttpPost]
public ActionResult Login(int carId)
{
...
Session["carId"] = carId;
...
}
Retrieving a variable from the Session object
public ActionResult Load()
{
...
int carId = Session["carId"];
...
}
Whilst this is a basic example, it gives you an idea as to how to store/retrieve simple types of data from a session.
For storing more information such as large objects you can use the [Serialize] class attribute outlined in my answer in this post.
Related
Im building an admin interface for a medical records management app.
My client has asked me for a way to easily select the patient the user wants to work with without having to select the patient everytime he wants to perform an action.
So, say for instance he wants to store a record for the patient's current status (weight, size, etc) and then assign the same patient to a different doctor or change the company the patient currently works for.. he doesnt want to select the same patient all three times... he wants a select dropdown for patients and perform the different actions for that patient.
Im thinking this should be somehow stored in a session variable.. I have a table of patients and Im using LinqtoSql classes.... what do you reccommend?? help please.
Sounds like you want to put something into Session--perhaps some of the basic "recent patient" information, such as a patient ID, patient name, etc.
Definitely take a look at this post on how to do it in a very graceful way.
You could use the session to keep a list of recently active patient records for a given users session. Every time the user selects a new patient simply add that patient's name to the "Recent" list. Since you can control the length of the session you could just allow the list to expire when the user's session does. As far as not making the user select a customer again, just have it auto-select the most recent (last entry) on the list of recent customers.
Personally I would consider caching as an option here. By the sounds of it you want to load ALL data for ALL patients which is fine for a small amount of data but will not scale gracefully.
Consider going to the database the FIRST time you need the patient's data, and getting your data from the cache for subsequent queries...
The best way is to have the id in the route.
Use something like this when registering routes:
routes.MapRoute(
"Standard",
"{controller}/{action}/{PatientId}",
new
{
controller = "home",
action = "index",
PatientId = ""
}
);
When you select a Patient you post to an action that sets the PatientID in the RouteParameterCollection and redirect to the the action that displays the form for changing the patient. this way you always have the patientID in the URL.
Using session has some drawbacks:
If you have 2 windows open both use
the same session. This might confuse
users.
You can not bookmark a page
Session is usually stored in memory
on the appserver. This might lead to
performance problems if extensively used.
Let's say I have a Web application implemented like a set of wizard pages to edit a complex object. Until the user clicks on the "Finish" button, the object doesn't get saved to the back-end system (a requirement), so in the meantime I have to keep the whole information about the object in some kind of a session state.
Also, some of the wizard pages have to show combo and list boxes with potentially large number of items. These items are fetched from the back-end system using a Web service.
Coincidentally, the wizard allows the user to freely jump from one wizard page to any other (using tab links on top of the form), so it's not a simple "next, next... finish" thing.
Additional constraint: the Web application runs on a Web farm and the customer is weary of using server-side session state. In the best case they want to keep the size of the session state minimal (they had problems with this in the past).
So basically there are two problems here:
How/where to keep data entered by the user in the Wizard?
Whether to cache the combo/list items received from the back-end and if so, where?
Options I'm considering:
Storing the object in a WebForms-like ViewState (by serializing it into the HTML page). This would also include the combo box items. Obviously, there could be a problem with HTML pages becoming very large and thus Web application will be slow.
Storing it into server-side session state, regardless of the customer's wishes and without knowing how the performance will be affected until it is tested on the actual Web farm (late in the project).
I cannot decide between the two. Or is there another alternative?
Why cache at all? You could just have the tabbed pages where each page is a div or panel and just display the current div relating to your tab. That way you dont have to keep track and process all the inputs when the user submits the form.
Is it possible to store the wizard data in a temporary table in the database? When the user finishes the wizard the data is copied from the temporary table and deleted. The temporary table includes a timestamp to remove any old uncompleted data.
As Daisy said, it doesn't have to be cached. You could also use hidden form fields. Because these could map to the same object on each controller action, you could progressively build the object through successive pages.
//Here's a class we're going to use
public class Person
{
public int Age {get;set;}
public string Name {get;set;}
public Person()
{
}
}
//Here's the controller
public Controller PersonCreator
{
public ActionResult CreatePerson()
{
//Posting from this page will go to SetPersonAge, as the name will be set in here.
return View();
}
public ActionResult SetPersonAge(Person person)
{
//This should now have the name and age of the person
return View(person);
}
}
//Here is your SetPersonAge, which contains the name in the model already:
<%= Html.Hidden("Name", Model.Name) %>
<%Html.TextBox("Age") %>
And that's pretty much it.
I can suggest a few more options
Having the entire wizard as a single page with the tabs showing and hiding content via javascript on the client-side. This may cause the the initial page to load slower though.
Caching the data at the server using the caching application block (or something similar). This will allow all the users to share a single instance of this data instead of duplicating across all sessions. Now that the data is lighter, you may be able to convince the customer to permit storing in the session.
There is a lot of resistance in the MVC community against using Sessions. Problems are that a lot of us developers are building login systems like a bank website. One could argue for hidden fields and that works for some situations but when we need to time a user out for security and compliance, then you have several options. Cookies are not reliable. Relying on Javascript timers are not reliable and are not 508 compliant as the goal should be to degrade gracefully. Thus for a Login, a Session is a good option. If you write the time to the client browser, to the server database or server file system, you still have to manage the time per user.
Thus use Sessions sparingly, but don't fear them. For the wizards, you technically can serialize hidden fields passing them around. I suspect the need and scope will become much greater and an authorization/authentication implementation with Sessions will be the crux of the application.
If you cannot use ajax (for validation & dropdowns and ability to convert wizard to tabbed page) and cannot use html5 (for dropdown caching and form state saving in local storage), then I think you are pretty out of available "best practices" and you have to resort to bad (or worse) one.
As MVC is opponent of WebForms regarding session usage, maybe you can use a workaround? For example, besides storing all these values in some temporary database records you need to clean up later, you could set up AppFabric extension for Windows Server and use it to store dropdown list items (and scope can be for all users, so if more users are using system at the same time you need only one call to web service to refresh cache), and also to temporary store your objects between steps. You can set your temporary objects in AppFabric to automatically expire so cleanup is not necessary. It can also be of help for speeding up other parts of your system if you extensively call another system over web services.
I've been dealing with the same issue and, while my requirements are a little simpler (keeping state for just a few strings), my solution may work for you. I'd also be interested in hearing others thoughts on this approach.
What I ended up doing is: in the controller I just dump the data I want into the Session property of the Controller and then pull it out next time I need it. Something like this for your situation:
//Here's the controller
public Controller PersonCreator
{
public ActionResult CreatePerson()
{
//get the age out of the session
int age = (int)(Session["age"]);
//do something with it...
return View();
}
public ActionResult SetPersonAge(Person person)
{
//put the age in the session
Session.Add("age", person.Age);
return View(person);
}
}
The thing I like about this is I don't have to put a bunch of hidden params around on my view pages.
The answer to this can be found in Steve Sanderson's ASP.NET MVC 2/3, and requires a reference to the MVC Futures assembly. This link to Google Books is exactly what he does.
In essence, you serialize the wizard data to the View. A hidden field is rendered storing all of the acquired information.
Your controller can work out what to do through the use of the OnActionExecuting and OnResultExecuted (to cater for redirects) to pass it to the next view.
Have a read - he explains it much more thoroughly than I can.
Our web application needs one common parameter in every action method.
In our case it is the customer account id and we need to support the following scenarios
a. A group of users might have the same account id which can be derived from the user profile.
b. Customer Support team should be able explicitly supply the account id of a customer and also should be able to switch the account on any page
We are trying to not to use asp.net session to store this kind of data.
Are there any other options to store and manage this kind of common parameter data?
Write it out as an ecrypted value to hidden field on your master page and supply the value to every view. When the user is in a a customer role, place a change account "control" on the page that is able to retrieve and update the account data -- via AJAX, perhaps -- to change the current account id. It might be easiest to do this with a custom base controller which gets the data via the ValueProvider directly and stores it as a property on the page rather than having it be a parameter to every method.
Use Routing for the value. So if you need to change the id you can use another URL or post it as a parameter.
Whenever you need the value just ask the ValueProvider for it.
In case it is blank - use the one from user profile.
Of course you'd better write small method that will do just that:
// Register route like:
route.MapRoute("ProvidesAccountId", "{controller}/{id}/account{accountId}/{action}.aspx")
// Property on the base controller
protected Account CurrentAccount {
get {
var accountId = ValueProvider.GetValue<int?>("accountId"); // GetValue is just a helper
if (accountId.HasValue)
return YourRepositor.GetAccountBy(accountId.Value);
return CurrentUser.Account;
}
}
Not to use current user's account hit the URL: Profile/123/account/Edit.aspx
To use another account you can hit the URL: Profile/123/account-456/Edit.aspx
You get the idea.
Cheers,
Dmitriy.
i have this code in my membership service class (taken from the asp.net-mvc sample app)
public MembershipUserCollection GetUnapprovedUsers()
{
MembershipUserCollection users = Membership.GetAllUsers();
MembershipUserCollection unapprovedUsers = new MembershipUserCollection();
foreach (MembershipUser u in users)
{
if (!u.IsApproved)
{
unapprovedUsers.Add(u);
}
}
return unapprovedUsers;
}
i now need a view to show this list of information and allow someone to approve them which will go back to the controller and set the IsApproved property to true.
Create a view which will generate a form containing label and checkbox for each member of the collection. You need to be able to get from the id of the checkbox to the user.
In the HTTP.POST Action method, iterate through the submitted fields looking for set checkboxes, when you find one set the corresponding user to approved.
Obviously the form can display arbitrary details for each user.
To use the inbuilt control helpers takes a bit more effort because you don't have a fixed size model to work with. To achieve something similar I:
Used a non-strongly typed view
populated ViewData["ids"] with IEnumerable<IdType> (which the view would loop over)
For each entry populated ViewData["field" + id] for each field I was displaying in the entity
In the view looped over the ids using ViewData["ids"] to call the HTML helpers with the id of the field.
(That was V1, in V2 I used model state so I could use the inbuilt validation error display support, but that doesn't really apply if you just want to select users.)
The POST processing was similar, repopulating the id list from the database and the looking up in the passed FormCollection.
I am trying to get the hang of MVC framework so bear with me.
Right now, the only thing I'm using the session store for is storing the current logged in user. My website is simple. For this example, consider three domain objects, Person, Meeting, and File. Users can log in and view a "members only" profile of a meeting and can add files to it, or view a meeting's public "profile" if they aren't logged in.
So, from the meeting's private profile, with a logged in user, I have a "add files" link. This link routes to FileContoller.Add(int meetingId). From this action, I get the meeting the user want to add files to using the meeting id, but after the form is posted, I still need to know which meeting the user is adding files to. That's where my question lies, should I pass the "currently interacting with" meeting through TempData, or add it to the Session store?
This is how I currently have the Add action setup, but it's not working:
public ActionResult Add(int meetingId)
{
try
{
var meeting = _meetingsRepository.GetById(meetingId);
ViewData.Model = meeting;
TempData[TempDataKeys.CurrentMeeting] = meeting; /* add to tempdata here */
}
catch (Exception)
{
TempData[TempDataKeys.ErrorMessage] = "Unable to add files to this meeting.";
return RedirectToRoute("MeetingsIndex");
}
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Add(FormCollection form)
{
var member = Session[SessionStateKeys.Member] as Member;
var meeting = TempData[TempDataKeys.CurrentMeeting] as Meeting; /* meeting ends up null here */
if (member == null)
{
TempData[TempDataKeys.ErrorMessage] = "You must be logged in to add files to an meeting.";
return RedirectToRoute("LoginPage");
}
if (meeting == null)
{
TempData[TempDataKeys.ErrorMessage] = "An error occurred. No meeting selected.";
return RedirectToRoute("MeetingsIndex");
}
// add files to meeting
TempData[TempDataKeys.Notification] = "Successfully added.";
return RedirectToRoute("AddFiles", new {meetingId = meeting.MeetingId});
}
Edit:
Based on most of the answers, can any one provide any examples on what kind of data (other than messages) should be stored in TempData vs Session?
TempData is session, so they're not entirely different. However, the distinction is easy to understand, because TempData is for redirects, and redirects only. So when you set some message in TempData and then redirect, you are using TempData correctly.
However, using Session for any kind of security is extremely dangerous. Session and Membership are entirely separate in ASP.NET. You can "steal" sessions from other users, and yes, people do attack web sites this way. So if you want to selectively stop a post information based on whether a user is logged in, look at IsAuthenticated, and if you want to selectively show information based on what type of user is logged in, you use a Role provider. Because GETs can be cached, the only way to selectively allow access to an action in a GET is with AuthorizeAttribute.
Update In response to your edited question: You already have a good example of using TempData in your question, namely, returning a simple error message after a failed POST. In terms of what should be stored in Session (beyond "not much"), I just think of Session as a user-specific cache. Like the non-user-specific Cache, you should not put security-sensitive information there. But it's a good place to stick stuff which is relatively expensive to look up. For example, our Site.Master has the user's full name displayed on it. That is stored in a database, and we don't want to do a database query for it for every page we serve. (An installation of our application is used in a single company, so a user's full name is not considered "security-sensitive.") So if you think of Session as a cache which varies by a cookie which the user has, you won't be far wrong.
The default TempData provider uses the session so there really isn't much of a distinction, except that your TempData is cleared out at the end of the next request. You should use TempData when the data needs only to persist between two requests, preferably the second one being a redirect to avoid issues with other requests from the user -- from AJAX, for example -- deleting the data accidentally. If the data needs to persist longer than that, you should either repopulate the TempData or use the Session directly.
You can use it as per your requirement. A clarification can be,
TempData Vs Session
TempData
TempData allow us to persisting data for the duration of single subsequent request.
ASP.net MVC will automatically expire the value of tempdata once consecutive request returned the result (it means, it alive only till the target view is fully loaded).
It valid for only current and subsequent request only
TempData has Keep method to retention the value of TempData.
Example:
TempData.Keep(), TempData.Keep(“EmpName”)
TempData internally stored the value in to Session variable.
It is used to stored only one time messages like validation messages, error messages etc.
Session:
Session is able to store data much more long time, until user session is not expire.
Session will be expire after the session time out occurred.
It valid for all requests.
N/A
Session varible are stored in SessionStateItemCollection object (Which is exposed through the HttpContext.Session property of page).
It is used to stored long life data like user id, role id etc. which required throughout user session.
TempData and session, both required typecasting for getting data and check for null values to avoid run time exception.
"It doesn't work" isn't very descriptive, but let me offer a couple suggestions.
Under the hood, TempData uses Session to store values. So there isn't much difference in terms of storage mechanisms or anything like that. However, TempData only lasts until the next request is received.
If the user makes an ajax request in between form posts, TempData is gone. Any request whatsoever will clear TempData. So it's really only reliable when you're doing a manual redirect.
Why can't you just simply render the meeting ID to a hidden field in your View form? You're already adding it to the model. Alternately, add it to your route as a parameter.
I prefer to maintain that kind of data in the page itself. Render meetingID as a hidden input, so it gets submitted back to the controller. The controller handling the post can then feed that meeting ID back to whatever view will be rendered, so that the meetingID basically gets passed around as long as you need it.
It's kind of like the difference between storing a value in a global variable before calling a method that will operate on it, vs. passing the value directly to the method.
I would suggest MvcContrib's solution:
http://jonkruger.com/blog/2009/04/06/aspnet-mvc-pass-parameters-when-redirecting-from-one-action-to-another/
If you don't want full MvcContrib, the solution is only 1 method + 1 class that you can easily grab from MvcContrib sources.
The TempData property value is stored in session state. The value of TempData persists until it is read or until the session times out. If you want pass data one controller view to another controller view then you should use TempData.
Use Session when the data need for the throughout application