I have an app that uses a large model. It takes five view pages to collect all of the data. I am keeping the model in session between view pages. Sometimes the session dies after inactivity in the middle of the process. When this happens, I want to push the user back to the Home page.
Here is part of my model:
private MemberRegistration MemberRegistration {
get {
if (HttpContext.Current.Session["RegisterModel_MemberRegistration"] == null)
{
return null;
>>>> I want to go back the home page here <<<<
}
return (MemberRegistration)HttpContext.Current.Session["RegisterModel_MemberRegistration"];
}
set {
HttpContext.Current.Session["RegisterModel_MemberRegistration"] = value;
}
}
public string FirstName {
get {
return MemberRegistration.FirstName;
}
set {
MemberRegistration.FirstName = value;
}
}
My problem is this. The model binding begins when the user clicks the "Next" button. The model binding happens before the [HTTPPost] controller action. If the session has expired, we hit the "return null" line. I want to send the user back to the home page right then.
A secondary question - is there a better way to do this other than using the Session. (Please don't suggest that we keep the partial registration in the database. I'd rather have the problems with keeping it in the Session than the problems that arise from having a partial record in the database.)
Thank you very much!
Alternative: Use jQuery UI and create Tabs. Use 5 tabs on single view page instead of 5 view pages - you won't have to break your model to pieces.
http://jqueryui.com/demos/tabs/
Related
I have a wish list, that is throughout the shopping pages. I need to know if this makes sense/the proper way of structuring.
Store the wish list as a session, when a user adds/deletes a new item it updates the session by an ajax call that just returns true/false if successful. On the partial view of the wish list component, I check for the session and cast it to my viewModel (which the session is based on) or serialize it for my knockout.
Let me know if this makes sense, otherwise I can post some code samples
It's hard to say without having a look at your basic structure, and not knowing you exact needs.
I don't know if you know this, but you can actually access the Session directly in Views:
#{
var wishlist = (WishList)HttpContext.Current.Session["Wishlist"];
}
It's fine to use Ajax to update it server side; and then you can return a partial view from the controller, to use however you like in the Ajax success call.
I hope this makes sense.
To begin with, if the wishlist is only supposed to exist for the duration of their visit then storing it in a session would be the best thing to do. However if the wishlist is supposed to live longer than a single visit and should be available to the user upon their return then I would suggest storing it in the database against the user's credentials/account (this is presuming they have an account).
As for the session itself, whilst you can access session data from a view I would not suggest it as you start to have a dependency on the session and before long you'll have code such as this scattered throughout your views.
var wishlist = (WishList)HttpContext.Current.Session["Wishlist"];
What happens when you want to change the way the wishlist works and instead have it database driven as you'd now like to persist the wishlist? You'll have to go through all of your views updating the references to the session.
Instead I would opt for registering your session with your IoC container of choice and injecting it using dependency injection, here is a simple example of how to register the session with StructureMap:
public class WebsiteRegistry : Registry
{
public WebsiteRegistry()
{
this.For<IUserWishlist>().HybridHttpOrThreadLocalScoped().Use(() => GetUserWishlistFromSession());
}
public static IUserWishlist GetUserWishlistFromSession()
{
var session = HttpContext.Current.Session;
if (session["WishList"] != null)
{
return session["WishList"] as IUserWishlist;
}
/* Create new empty session object */
session["WishList"] = new UserWishlist();
return session["WishList"] as IUserWishlist;
}
}
Now you're able to inject your wishlist into your controller and pass the data to your view via a view model. And as you're now programming against an interface instead of an implementation you could easily change how the wishlist is persisted without needing to change any code that references the wishlist.
public class WishlistController : Controller {
private readonly IUserWishlist userWishlist;
public void WishlistController(IUserWishlist userWishlist) {
this.userWishlist= userWishlist;
}
public ActionResult ViewProfile()
{
...
var viewModel = new UserWishlistViewModel {
wishlist = this.userWishlist.GetWishList()
}
...
}
}
I've written a more detailed example up in a blog post that might be of interest which can be read here. I hope this helps!
I'm developing an MVC3 application with EF and I wanted to make the UI fluent using jQuery ajax, the user will be able to navigate through the url, if he knows it or maybe he might receive a link pointing to a particular route, but, once the page is fully loaded it needs to be fluent, so I came up with one idea and I would like to discuss it here before I make the changes to the solution.
Here is what I came up with:
TestController.cs (Methods code has been omitted for simplicity)
public ActionResult Index() { ... }
public ActionResult Create() { ... }
[HttpPost]
public ActionResult Create(Test test) { ... }
public ActionResult Update(int testID) { ... }
[HttpPost]
public ActionResult Update(Test test) { ... }
public ActionResult Delete(int testID) { ... }
[HttpPost]
public ActionResult Delete(Test test) { ... }
So far it looks like most controllers. My views are as follows:
Views\Test\List.cshtml
Views\Test\Create.cshtml
Views\Test\Details.cshtml
Views\Test\Delete.cshtml
Now since I wanted to do it async: I've changed my List view so I could add, modify and remove from the list, so far is working like a charm. Plus, the user could still be able to navigate through the application using the url's, note that every link inside the application will perform an ajax request to do the actual work, there are no Route/Action links.
By now the application is working as expected, but now I came across something: there are views that I need to be ActionResult and PartialViewResult, that is because the user could type in the url: "/Admin/Test", which should return the full page, or could click on an anchor which will load only the content of the "/Admin/Test" and display it. To avoid the famous page inside page errors I wrote a function to send the request, and when the request arrives it selects only what I need, avoiding then the page inside page, and to duplicate views, but, the response is the whole page which, I don't need to say, it's not the best option, but since the application will be used by lan I didn't care too much about the payload of the response, but then I needed to write javascript code inside the views, so my solution was like null because using the jQuery selector to get only what I need the javascript wasn't there.
As for my new solution to solve my last solution:
I thought I might leave the original view as is, and create another view appending the word "Partial" after the original name, creating another method in the controller with the same naming convention, plus adding the new Route to my Route Table.
To wrap things up, what I need is the following:
- If the user types in "/Test" the response should be the entire page, loaded like the old days, screens flashing white and such.
- But if the user clicks the Test link in the navigation bar, the response should be async and refreshing only the content of my layout.
Any ideas? thoughts? suggestions?
In your actionmethod you can have
if (Request.IsAjaxRequest())
return PartialView("_somePartialView");
else
return PartialView("_someOtherPartialView");
I'm new to MVC and want to make sure I understand how to appreach a complex view I need to build.
I need to allow the user to add multiple Institutions on a page and each Institution can
have multiple trainings.
so the view model would be a list of institutions which contains a list of Trainings.
I will have a button that allows them to add multiple institutions and within the institution
section another button that will allow them to add multiple trainings.
On the postback method Do I simply loop through the institutions in the model
and for each institution loop through it's list of trainings to save them to the database?
You probably want to instead use a Controller like this:
public class InstitutionController : Controller
{
public ViewResult Index()
{
return View(); // Keep it simple, load data via JSON instead
}
[HttpPost]
public JsonResult Load()
{
// Go get Institutions etc
return Json(institutions);
}
[HttpPost]
public JsonResult Save(Institutions[] institutions)
{
try
{
// Save the institutions to the DB
}
catch (Exception ex)
{
return Json(new { Message = "Error." });
}
return Json(null); // Or some other way of saying it worked
}
}
That is, the Model you pass to the View is not persisted by the browser - instead it's briefly in server memory while the server generates a response.
In the example above however, you can create a View that uses for example jquery to load the Model from the server via JSON, which you can then persist in the browser for the life of the page. When the user makes a modification to an Institution or Instutions you can use a method like Save() above to send the new data or data changes to the server to be saved to the database.
I'm new in mvc and I have a question: If I have Index page (page with list of some object this is url http://localhost:6384/admin/) and I have actionlink button on which when user clicks he gets details of that object this is url http://localhost:6384/Admin/Apartman/details/1.
ID of obejct is 1, but if user change value 1 to some other value he will get some other object which maybe he shouldn't be able to see it.
What can I do to protect application?
The way i do it is simply checking whether the user has access to that object.
public ActionResult EditProfile(int id)
{
ApartmentViewModel objVM=MyService.GetApartment(id);
if(objVM.CreatedById==id)
{
return View(objVM);
}
else
{
return View("UnAuthorized");
}
}
I have a razor view that renders a html form and it posts to the server.
If the form values are right then it gets saved to database.
After insertion, I redirect to another view where user can make further changes.
Right now the user can hit browser back button and resubmit the form to create another record in db.
How do I prevent duplicate submission in my MVC app?
One solution is to put a hidden "token" field on the form that's generated randomly when the form loads. When you see that token come back on creation store it somewhere temporarily (in session if you're using sessions for example). If you see the same one again, you can assume the same form was submitted twice quickly together.
Create a cookie to represent that particular page when it succeeds. If it is replayed with the cookie (which the browser would now send over with every request) you know not to allow the new attempt.
Redirect the user to another HttpGet action after handling the post request.
So that when the user refreshes the browser the post action will not be called again.
return RedirectToAction("YourActionMethod");
Although client side validation is possible, it is not secure enough.
I am not sure if this method applies to MVC 3, but what i did is implement a ActionFilterAttribute
here is the implementation:
public class PreventFrequentCallsAttribute : ActionFilterAttribute
{
public int DelayRequest = 5;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.HttpContext.Request;
var cache = filterContext.HttpContext.Cache;
var originationInfo = request.ServerVariables["HTTP_X_FORWARDED_FOR"] ?? request.UserHostAddress;
originationInfo += request.UserAgent;
var targetInfo = request.RawUrl + request.QueryString;
var hashValue = string.Join("", MD5.Create().ComputeHash(Encoding.ASCII.GetBytes(originationInfo + targetInfo)).Select(s => s.ToString("x2")));
if (cache[hashValue] != null)
{
filterContext.Controller.ViewData.ModelState.AddModelError("ExcessiveRequests", "Excessive Request Attempts Detected.");
}
else
{
cache.Add(hashValue, originationInfo, null, DateTime.Now.AddSeconds(DelayRequest), Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
}
base.OnActionExecuting(filterContext);
}
}
later, in the target controller, just add this attribute:
[PreventFrequentCalls(3)]
public PartialViewResult LogOn(LogOnViewModel model)