I have this code in my Controller:
List<string> order = new List<string>();
[Route("Reservations/Overview/Refresh/id")]
[AllowAnonymous]
public JsonResult AddOrder(string id)
{
if(!order.Contains(id))order.Add(id);
return Json($"ok", JsonRequestBehavior.AllowGet);
}
[Route("Reservations/Overview/Check/id")]
[HttpPost]
public JsonResult Check(string id)
{
if (order.Contains(id))
{
order.Remove(id);
return Json(true);
}
else return Json(false);
}
Everything is working but my global order list is always empty. Why?
HTTP is stateless. Each request instantiates a new instance of the controller class, which wouldn't reflect any changes made to class-level variables on a previous instance (which has long since been disposed) from a previous request.
Basically you need to persist your data somewhere. There are a variety of places to do this:
Session state
Static values
A database
A cache mechanism
On the page itself (posted back with form data)
Cookies
A file
and so on...
Each option is going to have its pros and cons, depending on the full set of functionality you need. Session state may be the simplest approach to get you started. On the page itself may be considered more RESTful and, thus, more scalable/portable. A database would be more secure than on the page itself because users can modify anything on the page. Etc.
But the point remains regardless of which option you want... The data needs to be persisted (saved) somewhere in order to be able to read it again at a later time.
Global variables are not persisted across multiple requests to the controller. You can persist them to a database, or store the orders in session:
Session["Orders"] = orders;
You have to store the updated list of orders to session every time you modify the list or data within the list in any way. Session is per user; just be careful how much data you put in session if you do. If you use a database, you need to persist the record change anytime a value changes on a record, or when creating a new record.
Related
I have a project with a form that is completed over several parts. E.g. Part 1 might include your name, and part 2 your address.
In MVC I have several views which represent each of the parts and one model to hold all the form data.
I would like to save the form data at the completion of each part, and retrieve the saved record on the next part (so that the whole model is available at each stage).
I am using Post-Redirect-Get to avoid the 'form may be resubmitted' warnings from browsers using the back and forward buttons.
Controller code
[HttpGet]
[Route("Form/Part/{id}")]
public ActionResult Part(int id, int? recordId)
{
var model = new MyVM();
if(recordId.HasValue) {
// get record from DB
// map record to 'model'
}
return View("Form.{id}", model);
}
[HttpPost]
[Route("Form/Part/{id}")]
public ActionResult Part(int id, MyVM model)
{
// map model to entity
// save/update entity
return RedirectToAction($"Form/Part/{id + 1}", new { recordId = recordId })
}
My question is how best to retain the ID of the newly created / updated record and pass it to the next form part.
Whilst passing the recordId as a routeValue works, it breaks when I use the browser back button as the recordId in the querystring is lost.
I have considering storing this in the session perhaps, but don't really want to bring the HttpContext into play if I don't have to for testing reasons.
Is there a recommended way to achieve this kind of thing? Any thoughts on the various ways to keep track of the current record would be greatly appreciated.
You can either use the TempData or Session to pass Data between requests.
I Would create a big class / object with all the fields from the wizard, throw it in the session eg key "MyWizardData" and retrieve it in the other parts of the wizard.
After
// save/update entity
you have the ID, throw it in the session and grab it back in the next action.
If you dont want to use the session, get the id after save / update and store it in a field in the database in the user table. Or just store the UserId with your created entity row. Then you can grab it in any action.
This question might sound dumb, but I am new to asp.net mvc and can't find the answer to my question.
In my application ( a game) I have a model of the game GameModel (it contains a multidimensional array). What I want is to be able to use the same object in every controller I use. So I create it once and after that use it in every controller function.
Basically there is one view, and all other functions in the controller edit the object with functions of the model.
My idea was put the object in a session variable, make a function to check the session variable if the object is not set set the object. But this does not look logic to me, hopefully someone has a better solution.
According to your question, you want to keep track of a user's data (game data).
Storing GameModel in Session variable make sense for that scenario.
If you see yourself calling that Session variable from a lot of places, you can create a BaseController and keep it there. Then inherit all controllers from it.
For example,
public class BaseController : Controller
{
public GameModel CurrentGameModel
{
get
{
var model = Session["GameModel"] as GameModel;
if (model == null)
{
model = new GameModel();
Session["GameModel"] = model;
}
return model;
}
set { Session["GameModel"] = value; }
}
}
public class HomeController : BaseController
{
}
Note: You have to keep in mind that if Application Pool recycles or Application crashes, all data stored in Session variable will be lost.
If you want to persist data, you need to store in persistent storage like database.
I don't understand why you don't think Session looks good. It's purpose is exactly keeping data per user througout multiple requests.
You could also return the state of the game to the client using hidden fields. That would be even better than Session, given that your game state doesn't change in the server, as a response to someone else's action.
And finally you can use a static property of a class. Static properties in ASP.NET are kept alive througout the application lifecicle and are visible equally to all users. Meaning, if a user writes something there, another user can read it. You can allocate data per user using a Dictionary<>, though, where the key is the user Id.
This is something happening everyday
I'm looking for a better way of grabbing the values from view into the action, since the controller create and destroy based on http request, is there a good way to pass all the params thru?(By that I mean more than 5 params)
I think about session but there is a chance pepple lose their session and the important value gone, besides session, is there any other way around?
I would first consider whether your application needs to save that much information between views. MVC is a REST based architecture, and is typically designed to be stateless.
With that said, your options for passing around state with a user boil down to Session, Cookies, and Database.
I would create a static class that stores and retrieves its data from session.
public static class CustomPersistStore
{
public static CustomClass Current{
get{
var instance = HttpContext.Current.Session["key"] as CustomClass;
if(instance = null) {
instance = new CustomClass();
}
return instance;
}
}
}
If you want to pass values from the View to the Action the best practice is to use strongly typed views.
http://howmvcworks.net/OnViews/BuildingAStronglyTypedView
SITUATION:
I have a Model and based on a users Role I want to allow the user to only update certain parts of the model. Lets say the Model has three fields. (My Model is obviously more complex than this)
MyObject
Field1
Field2
Field3
My View looks something like this:
Html.TextBoxFor(#Model.Field1)
Html.TextBoxFor(#Model.Field2)
#if(UserIsAdmin())
Html.TextBoxFor(#Model.Field3)
else
#Model.Field3
Bearing with me on the syntax (and the poor design of the example), you can see what I'm trying to do. Upon the user posting the form my controller would just take the MyObject and save it back to the database, we are using EF.
QUESTION:
My question is, is there a way to stop a user from forging a POST to be able to save data he/she should not be able to. My current idea would be to do a check in the controller to see if the user modified values he should not have. Or I could save fields individually, but neither is a convient solution.
Is there a better one?
Thanks!
Additional Info:
Not sure if this artical is relevant at all: http://blog.stevensanderson.com/2008/09/01/prevent-cross-site-request-forgery-csrf-using-aspnet-mvcs-antiforgerytoken-helper/
All three fields are from the same database table and I'm using EF to get and save the entity.
You want to make sure the user is only able to update permitted fields.
You decided that the way to achieve this is to prevent the user "forging" a response using e.g. firebug, or F12 developer tools, or GreaseMonkey, and have asked how to do this.
But the correct/best method is to check which fields the user is attempting to update, and only update those which he is permitted to update. Then it doesn't matter if they forge the request or not, they still won't be able to access anything they shouldn't. In other words, check access rights at the point of access.
Anti-forgery tokens are there to solve a separate problem, namely XSRF.
Use a viewmodel that accepts only the fields that should be updated and then populate the model with those values. You could use something like AutoMapper for mapping between the two.
My current idea would be to do a check in the controller to see if the user modified values he should not have. Or I could save fields individually, but neither is a convient solution.
You're on the right track with that idea. A typical Add() operation would look like this:
public class FooController : Controller
{
public ActionResult Add(FooViewModel viewModel)
{
if (ModelState.IsValid)
{
FooDataModel dataModel = FooMapper.MapToDataModel(viewModel, User);
FooRepository.Add(dataModel);
}
}
}
Like #VimalStan said, your FooViewModel is then a model that contains only the fields you want to let the user update. Also this still doesn't solve your problem, which should be done in your mapper (in this case FooMapper) and still check every field as #Ben suggested:
public static class FooMapper
{
public static FooDataModel Map(FooViewModel viewModel, IPrincipal user)
{
var dataModel = new FooDataModel();
dataModel.Field1 = viewModel.Field1;
dataModel.Field2 = viewModel.Field2;
if (IsAllowedToUpdateField3(user))
{
dataModel.Field3 = viewModel.Field3;
}
return dataModel;
}
public static bool IsAllowedToUpdateField3(IPrincipal user)
{
return false; // your logic
}
}
After a pair programming session, an interesting question came up which I think I know the answer for.
Question: Is there any other desired way in ASP.NET MVC to retain 'state' other than writing to database or a text file?
I'm going to define state here to mean that we have a collection of person objects, we create a new one, and go to another page, and expect to see the newly created person. (so no Ajax)
My thoughts are we don't want any kung-fu ViewState or other mechanisms, this framework is about going back to a stateless web.
What about user session? There are plenty of valid use cases to store things in session. And what about a distributed caching system like memcached? You also seem to leave out the query string - which is an excellent state saver (?page=2). To me those seem like other desirable methods to save state across requests...?
My thoughts are we don't want any kung-fu ViewState or other mechanisms, this framework is about going back to a stateless web.
The example you provided is pretty easy to do without any sort of "view state kung fu" using capabilities that are already in MVC. "User adds a person and sees that on the next screen." Let me code up a simple PersonController that does exactly what you want:
public ActionResult Add()
{
return View(new Person());
}
[HttpPost]
public ActionResult Add(PersonViewModel myNewPersonViewModel)
{
//validate, user entered everything correctly
if(!ModelState.IsValid)
return View();
//map model to my database/entity/domain object
var myNewPerson = new Person()
{
FirstName = myNewPersonViewModel.FirstName,
LastName = myNewPersonViewModel.LastName
}
// 1. maintains person state, sends the user to the next view in the chain
// using same action
if(MyDataLayer.Save(myNewPerson))
{
var persons = MyDataLayer.GetPersons();
persons.Add(myNewPersion);
return View("PersonGrid", persons);
}
//2. pass along the unique id of person to a different action or controller
//yes, another database call, but probably not a big deal
if(MyDataLayer.Save(myNewPerson))
return RedirecToAction("PersonGrid", ...etc pass the int as route value);
return View("PersonSaveError", myNewPersonViewModel);
}
Now, what I'm sensing is that you want person on yet another page after PersonSaveSuccess or something else. In that case, you probably want to use TempData[""] which is a single serving session and only saves state from one request to another or manage the traditional Session[""] yourself somehow.
What is confusing to me is you're probably going to the db to get all your persons anyway. If you save a person it should be in your persons collection in the next call to your GetPersons(). If you're not using Ajax, than what state are you trying to persist?
ASP.NET MVC offers a cleaner way of working with session storage using model binding. You can write a custom model binder that can supply instances from session to your action methods. Look it up.