What is the "best" way to handle alternately Post and Get Actions? - asp.net

I am trying to build sth pretty simple, but I try to do it the correct way. But I struggle to figure out what is best.
I have a process chain where the user has to fill in some fields in different forms. Sometimes it depends from the user inputs which form the user is shown next.
[HttpGet]
public IActionResult Form1(Form1Vm f1vm)
{
return View(f1vm);
}
[HttpPost]
[ActionName("Form1")]
public IActionResult Form1Post(Form1Vm f1vm)
{
//process the data etc
//prepare the new viewmodel for the next form view (f2vm)
//Option1:
return View("Form2", f2vm);
//Option2:
return RedirectToAction("Form2", f2vm);
//for Option 2 I would need an additional HttpGet Action Method in which I
//would have to call Modelstate.Clear(); in order to not have the
//immediate validation errors on page load
//also all the properties of my viewmodel are passed as get parameters
//what looks pretty nasty for me
}
//More form views action methods should be added here...:
What is the better way? As mentioned in my comments above I have quite a big disadvantage for using the RedirectToAction option. However if I use the direct View(); call, I don't take care on https://en.wikipedia.org/wiki/Post/Redirect/Get and the user cannot simply refresh a page without getting a warning that his form is submitted once again.
Do I miss another way or don't see something obvious?
Edit: I just thought about a 3rd way, which I have seen quite often: Not transfering the whole VM to a HttpGet method but only the ID. I'd then have to load all the data stored previously directly from the db, map it again to my new VM and then call the View(); with this VM. Right now I think this is the "best" solution, however I feel like it is pretty laborious...

As per the dicussions, I would suggest using depending on your preference :
1) Save to db at the end of each form post and as you suggested use the I'd to redirect to a GET.
2) Depending on the the number of form pages and your requirements, retrieving values that a form needs on the get would be standard practice. This ensures that if a user drops off a form at any stage you can then start them off where they left off.
3) I wouldn't setup the viewmodel for the next form in the post of the previous. Generally as part of the single responsibility principle you want to ensure that your methods have only one reason to change.
4) PostRedirectGet pattern should be implemented with this to ensure data is not saved multiple times if a user refreshes after a post.

Related

Simulate previous request

In my application there are sometimes in that I want to get a user request, save it in some place and then, in a next request, simulate saved request instead of real request, is that possible?
Use any web debugger. I use Fiddler 2.6.2 myself.
Find it here, https://www.telerik.com/download/fiddler
Be warned - there is no robust way to do it (there are a lot of edge cases to consider) but hopefully this will get you started:
Take Request.Form.ToString() and save it against the user somewhere. For testing I suggest putting it in a hidden field, but at some point you would probably need to move that to a database and associate that with the user somehow.
You will need to have a field that will tell you whether the previous request should be replayed. Using a query string parameter like ?replay=true will work. Then pull it into your page:
protected bool IsReplayRequest
{
get
{
return bool.Parse(this.GetValue(p => p.Request["replay"], "false"));
}
}
To simulate the request you will need to override the page's DeterminePostBackMode. This is one of the first methods ASP.NET calls to start the postback process. Use the saved request form converted into a NameValueCollection. Something like this:
protected override NameValueCollection DeterminePostBackMode()
{
if (IsReplayRequest && SavedFormData != null)
{
if (!string.IsNullOrWhiteSpace(SavedFormData.Data))
{
return HttpUtility.ParseQueryString(SavedFormData.Data ?? string.Empty);
}
}
return base.DeterminePostBackMode();
}
Where SavedFormData is just a class which holds the request form and the user information.
With those 3 steps, you should be able to succesfully simulate a previous request for a simple page
Now for the potential problems:
First, the viewstate might get in the way. If you have any listboxes or data grids or authentication etc, you will then need to save the viewstate with the form data. for that you will need to override the page GetStatePersister() method (Is it possible to make a custom implementation of ViewState?).
Second, once you have simulated a request, you need to make sure a) no data is corrupted and b) you remove the "replay" query string. Otherwise the request will play over and over again.
Third, depending on how you store the request form, you will have to think about how to associate it with the user/browser/session/window. Dealing with each one of those has it's own problems.
Once you've got everything above solved, you will successfully simulate a saved request!

ASP.NET MVC objects lost on post method

I just came across this topic in a current project and I would like to know the best way to handle the "problem" of objects getting lost, when using the HttpPost method when submitting a form. Thoughout the web and the posts on stackoverflow I gathered some ways to deal with it.
So, once I got that a view model gets objects or lists from the HttpGet request they are lost because of the stateless nature of http. I now have seen different approaches to get that objects back when passing the view model back to the view.
Store objects in hidden field
#Html.HiddenFor(m => m.Object)
This seems to be working fine if the view models property is serializable. But what if there is logic behind the objects? Will this work for all scenarios? And on the other hand, I don't want every object inside my view model, just the ones for display and the user can interact with.
Get the object via AJAX and HelperMethods
#Html.Some3rdParty().ComboBoxFor(m => m.List)
.DataSourceUrl(#Url.Action("GetComboSource"))
[ComboBoxSource]
public ActionResult GetComboSource()
{
var data = Service.GetStaticSource();
return Json(data);
}
Using 3rd party libraries like Kendo UI MVC I can easily put a .DataSourceUrl() HtmlHelper on my control for requesting a controller to provide the datasource. Or just using jQuery's $.get() method. The problem is: What if I can't use such a library or the requirement says, that JS has to be avoided as much as possible?
Get the object back in the HttpPost Controller action
[HttpPost]
public ActionResult Edit(ViewModel viewModel)
{
var tempViewModel = Service.GetViewModel(viewModel.Id);
viewModel.Object = tempViewModel.Object;
return View(viewModel);
}
This one seems a bit brittle to me. On the post action I have to call whatever service provides the view model, extract certain objects and attach them to my recieved view model.
Currently that's all I can think of, but in short:
I'd like to know, what is the best practise to handle it throughout a project and what's the best way for maintainability?
For me it seems there is no perfect solution so far. How do you do it in your projects?
Avoid solution 1: you will weigh your requests, the data can be forged and if your object is not serializable (or the to string method does not provide useful string) you will have some problems. If you want to use something similar prefer Session
Solution 2: I use ajax populating when the input have to be filtered depending of other controls value. If it is not the case you will have controller with lot of actions and not easily maintanable.
Solution 3 is the one I use and I prefer because it is more DRY, more secure (no forgery), and keeps the controller light.
Just change
<input class="ui-input-text " type="text" id="PlateNo" tabindex="2" name="PlateNo">
to
<input class="ui-input-text " type="text" id="PlateNo" tabindex="2" name="PlateNo" value="#Request["PlateNo"]">
You can be show on the textbox sending value for this input.
Only add value="#Request["yourinputname"] to your input.

MVC4 - Relationship between a View and a Controller

I'm having difficulty grasping the concept of MVC within .NET. I'm creating a basic Blog application, mainly to gain insight and skills with MVC. I just don't quite get some of it. The part I am currently unclear about is the relationship between a Controller and View. It would clear the matter up if someone would answer me this.
I have a View called TestA which corresponds to my Controller ControllerTestA. Now I've added the following inside the ControllerTestA.
public ActionResult TestA (){ //do something }
Now I need to know if all my postbacks in whatever form from view TestA will have to go through my TestA Controller method. So essentially I could have different postback with different parameters for different reasons. Some in use with one postback and others in use for another. Is that how it is done?
Would love some assistance here.
You are missing a crucial part of the relationship here, which is routing. You are speaking in terms of WebForms using terms like Postback; don't do that because you'll end up confusing yourself.
The best way to think about MVC is in Requests and Responses.
Let's look at how a request (high level) happens in an MVC application.
Request
Request hits the server with a url ex. /hello/world
That url is used to match any entries in your route table
When a match is found, that route defines basic values like what controller and action should be called.
The controller is created, and that action is called with the route values and other request properties (querystring, session, etc...).
Response
We are now in the controller action, run the code you need to fulfill the request
Pass the data to the View
The view is determined by convention and your ViewEngine
The view is then rendered and written to the response.
The request/response is finished.
Note
This whole process is determined by the route, and the relationship between the controller and view are trivial. Where the form is posted to is determined by you in the view by using helper methods that determine what route to hit in the next request/response flow.
Some Helper Methods.
#Url.Action("index", "home");
#Html.ActionLink("index", "home")
#using (Html.BeginForm("create", "home")) { }
To sum it all up, the relationship between the controller action and view is really facilitate by your routes. Once you have a grasp of how to create them, then you will better understand how to manage the interaction of your application. Hope that helps. :)
There is no such thing as "Postback" in MVC. In contrast to WebForms, a view only renders HTML to be sent to the browser. As soon as any type of request is issued by the browser, it goes to the controller, not to the view.
As for the relationships:
If you define a TestAController (note: Not "ControllerTestA"), it serves the "/TestA/*" URL's. If you have a method TestA in there it will serve "/TestA/TestA".
If your method returns View(someModel) it will look for a view named TestA.cshtml/TestA.aspx, named like your method, within a folder Views\TestA (named like your controller, without the "Controller" suffix)
The view will render the HTML based on the someModel passed by the controller.
Within the view you may call other URL's or post data to some. This closes the circle.
As for the parameters or overloads, there are some restrictions:
You can define overloads for GET vs. POST vs. PUT vs. DELETE. You will need to annotate the methods with the according attributes though.
However you cannot define multiple overloads of the same method name for POSTs with different sets of parameters. You will need to make your POST method signature such that parameters can or cannot be sent to the server:
Example:
public ActionResult TestA(
string someOptionalParameter, int? someOtherOptionalParam)
{
if (string.IsNullOrEmpty(someOptionalParameter)) { ... }
if (someOtherOptionalParam == null) { ... }
}
The model-mapper will set your parameters to null if they are not posted to the server.
Like Khalid already mentioned - you should not mix up the concepts of MVC and WebForms. They are very different. MVC has no such thing as a "view state" which could be posted to the server. It has no WebForm-like lifecycle for the ASPX (or CSHTML) pages.
If you have a form in a view, then that form has a url to which it will post to. This URL is in the Html.BeginForm method in your view.
The form will then be posted to the appropriate controller methond in the approoriate controller
So if BeginForm starts like this:
using (Html.BeginForm("AddProduct", "Product"
Then the action method "AddProduct" in the controller Product (ProductController is the class name) will be called.

ASP.NET MVC rendering common data such as notifications

In my requirement, all views should be able to display notifications based on what happened within Actions in the controllers. The implementation as it stands is in a BaseModel which is inherited by all Models needing to show notifications. Then, any view with this requirement simply calls:
#{ Html.RenderPartial("MessageCtrl", Model); }
And a notification is rendered. This works fine in cases where an Action is returning a view directly, and is usually handled like this in an Action:
model.SetMessage(response);
return View(model);
...where SetMessage updates the notification related properties of the model from an object returned from my service. All responses implemented an interface I created called IResponse, so messaging can be handled throughout.
The problem arrises when I need to return a View from a different Action and still show a notification.
For example, on my EditUser action, if edit was successful, I want to show the Index view. To achieve this from the EditUser action, I need to return RedirectToAction (because simply returning the view will return the correct view with the incorrect URL indicating it is EditUser when it is not). However, RedirectToAction loses all notion of my model and BaseModel, so by the time it redirects, it has lost my notification. I know I can use RouteValues, but that defeats the purpose of having a common base model driven notification approach. I even looked at the ViewBag (usually I dismiss it as the ugly part of MVC), but found that ViewBags only persist for the life time of a single request, and using RedirectToAction, the ViewBag is empty again by the time it hits the target Action. Another thought was to store it in a session object, but that sounds downright ugly to me.
This seems like its a pretty standard requirement. Is there a standardised approach to passing common data (like notifications) around to different views asside from through the querystring through a RedirectToAction?
I think you are looking for TempData which only persists data in session across one redirect.

ASP.NET MVC - How to Create a RESTful Controller Scheme?

If this is a duplicate, please let me know - because i had a quick look and couldn't find anything that really answers my question.
I'm experimenting with ASP.NET MVC 2. Now coming from a Web Forms background, i only really dealt with HTTP GET and HTTP POST.
I'm trying to see how i could apply GET/PUT/POST/DELETE to the respective CRUD operations on my repository (Find, Insert, Update, Remove).
Say i have a single controller, with the following action methods:
[HttpGet] // GET: /Products/{id}
[ActionName("Products")
public ActionResult Get(int id = 0) { ... }
[HttpPut] // PUT: /Products
[ActionName("Products")
public ActionResult Put(Product product) { ... }
[HttpPost] // POST: /Products/{product}
[ActionName("Products")
public ActionResult Post(Product product) { ... }
[HttpDelete] // DELETE: /Products/{product}
[ActionName("Products")
public ActionResult Delete(Product product) { .. }
Couple of questions on that - is that how you would name/separate the action methods? Should i be passing through the entire model (Product), or just the id?
The problem i'm having is i'm not sure how to handle invoking these action methods with the relevant HTTP Verb in my View.
At this stage, I'm thinking i would have 3 Views:
"Index" - bind to IEnumerable<Product> model, listing all products, with "Edit", "Details" and "Delete" links
"Single" - bind to single Product model, listing all details for a product, with an "Update" button.
"New" - bind to single Product model, with form for creating product, with an "Create" button.
So - my question is, how do i specify i want to invoke a particular controller method with a specific HTTP Verb?
With Html.BeginForm, you can specify a FormMethod enumeration - but it only has GET and POST.
How can i perform a PUT and DELETE command?
Will i need a seperate View for each HTTP Verb?
If i have a link called "Delete", can i invoke a HTTP DELETE command to my controller, or does it need to be redirected to a new View with the form action delete?
Or, is this a silly/overkill design in the first place, should i just stick with "GET" and "POST"?
I'm (fairly) new to this style of web development (REST), so please be kind. :)
UPDATE
So i came across this interesting article from Stephen Walther, regarding this very topic.
He indicates a HTML Form only supports GET and POST (because i'm new to REST-style web development, i did not even know this, to which i am partially ashamed).
Apparently the only way to invoke a Controller action with PUT/DELETE is to do it with AJAX. Seriously?
So what should i do here, should i stick with GET/POST, or should i create a JavaScript file which wraps the underlying XmlHttpRequest code behind a nice function?
Which way are ASP.NET MVC developers leaning? Surely someone has asked themselves this same question.
The reason that you're mostly familiar with GET and POST is because a regular old browser can only GET or POST a URL, especially when submitting a form.
Setting up your controllers this way, you're looking at something more along the lines of a REST API vs. a web site at least in spirit.
And yes, you are correct that only AJAX requests can set their request methods explicitly, so you're good to go if you'll be performing actions like deleting products via AJAX (in which case I would suggest only passing the ID since it is much lighter than serializing the entire product and you'll likely have easy access to the ID in your markup).
Here's the thing. If you are building a modern web application you're probably using AJAX and without getting too dogmatic you should expect your users to have JavaScript. If you want to do CRUD without AJAX then you can always POST a Product or use GET for a delete method with the ID in the URL (caveat to that, make sure the Delete method is protected behind authorization or a web crawler will gladly consume your GETs and delete all your data...)
If you don't want to build a REST API then don't sweat the fact that you're not using DELETE (just use GET with the ID in the URL) or you're not using PUT (just use POST with the product entity in the POST body).
REST is a great style for building APIs (no presentation tier, data sent in raw format to clients who consume them, could be a phone or a website, etc.) over HTTP. HTTP is great on its own for building web pages. Use what you need for what you need it for.
If you want to build a REST API to be used by both other people AND your web site, then do that, and just have your site's controller action methods call your API methods directly. I do that all the time. Use a web client like Hammock to make it easier on you.
Personally I stick to as simple as possible full requests, instead of going AJAX all over. I use ajax, but in specific places where it enhances the experience (and I do with a progressive javascript approach).
Because of the above, I always just use GET and POST.
As for the naming, for those I'd do: Get, Update, Create, Delete, which makes it pretty clear. I actually use more like an action name that's related to what the operation does, but for simple things those work fine.
HtmlHelper.HttpMethodOverride Method (System.Web.Mvc)
How about this?

Resources