I have two methods:
public ActionResult EditNote(int? id)
{
NotesModel edit = NotesProcessor.LoadNote(Convert.ToInt32(id));
if (edit.Author != Session["UserName"].ToString())
{
edit.Id = null;
edit.Title = null;
edit.Note = null;
edit.Author = null;
return View(edit);
}
return View(edit);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditNote(NotesModel model)
{
if(model.Id == null) NotesProcessor.SaveNoteEdit(model);
else
{
model.Author = Session["UserName"].ToString();
NotesProcessor.SaveNote(model);
}
return View();
}
I've simplified the code to only show the problematic part which is:
A logged in user is trying to edit a note which they're not allowed to because it was made by another user. So all the values are set to null so the user will create a new note. The first method will receive an id to search the database for the note the user is trying to edit.
In the next HttpPost method however model.Id is still the same as the integer received by the first method despite changing all the values to null in the first method.
The first method is called from the views page like this:
#Html.ActionLink("Edit", "EditNote", new { id = Model.Id })
Anyone got an idea whats going on here?
You are actually not posting anything to the system as it seems, you are just calling the HttpGet method via URL and then clearing out the view model data at page level only. So, it seems your HttPost method is not even hitting. Try debugging and see whether your HttpPost method gets hit.
You don,t even need to create two methods for what you want to achieve. All you need is just single HttpPost method.
For HtpPost method to be called, you need to either create Form control at UI level with action type as post or you need to hit HttpPost method via JavaScript / JQuery
I hope this helps.
Related
I have a Controller with some actions on it as follows:
[HttpPost]
public ActionResult Create(CreateModel model)
{
if (model.SelectedCustomers.Count > 0 &&
model.SelectedVersions.Count > 0 &&
!string.IsNullOrWhiteSpace(model.ScriptName) &&
!string.IsNullOrWhiteSpace(model.ScriptText))
{
Script script;
...save to database...
return Edit(script.Id); //<---------Return other view here
}
else
{
...
}
}
[HttpGet]
public ActionResult Edit(int? scriptId)
{
return View();
}
After the Create action runs, and saves my model to the database successfully, I want to send the user to the Edit view for the newly created script. When I use the code above, specifically return Edit(script.Id); it just sends the user back to the Create view instead of the Edit view. When the user navigates to the Edit action directly, or through the result of an Html.ActionLink pointed at Edit everything works correctly.
What am I doing wrong?
This isn't doing what you think it does:
return Edit(script.Id)
It's not actually telling the framework to go to that action. It's just returning the return value of that method. Purely a C# concern before any components of the ASP.NET MVC Framework are involved at all. And what is that return value:
return View()
So the former is really functionally the same thing as the latter. And any time you use return View() in ASP.NET MVC, the framework will determine that view by examining the action currently being called, which in this case is Create.
What you want isn't to return the Edit view (even if you do, in this case, the user is still on the Create URL, which will cause confusion). What you want is to return a redirect to tell the client to request that next action:
return RedirectToAction("Edit", new { scriptId = script.Id });
You can always call RedirectToAction and return that action result. That will inform the browser to redirect to the different action.
I think you will need something like this:
return RedirectToAction("Edit", new { scriptId = script.Id });
Calling Edit directly is no different than calling a method.
You can do with this RedirecToAction with input parameters.
return RedirectToAction("Action", new { id = 12 });
In Your Case:
return RedirectToAction("Edit", new { scriptId = script.Id });
I have this neat model, which is populated on a screen and inserted to a database on successful validation. To make the web app easier to use, I make it redirect to a specific URL after it posts the data. To do that, I pass the URL as a hidden field (the URL is dynamic and depends on the Get request). Of course, on failed validation the model is returned and textboxes and other editors are repopulated, but the hidden field with the URL is not. How can I make it repopulate after validation error, without it beign the part of the model?
Here's some of my code:
-get method:
public ActionResult Create()
{
ViewBag.returnUrl = System.Web.HttpContext.Current.Request.UrlReferrer; ....
-post method:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Issue_ID,Issue,Case_Status,
Issued_by,Issue_Date...,HelpDesk_Service_Request_Ticket_Number")] Case #case, string returnUrl)
.
.
.
if (ModelState.IsValid)
{
db.Cases.Add(#case);
db.SaveChanges();
if (returnUrl == null)
{
return RedirectToAction("Index");
}
else
{
return Redirect(returnUrl);
}
}
return View(#case);
Thanks in advance!
From you question I understand you want to pass the return url value from one action (GET) to another (POST). You can store the value in TempData
TempData["returnUrl"] = new Uri("<return url>");
and then try accessing it using
var returnUrl= TempData["returnUrl"];
Note that once the value is read from TempData, it is automatically removed from the collection. For retaining the value you can use keep() or peek() method. Please refer a similar question answered here
Viewbag only lives for current request. You need to use TempData instead.
Please check this thread Viewbag passing value
What am I doing wrong with my MVC code here ? The Index view includes a form that submits to itself, what I'd like is the controller to process the submitted form and then return to the View.
What actually happens is the form is processed correctly, but the View returned is as if nothing happen (e.g. ids that have been deleted are still shown). If I manually refresh the page though, it displays correctly again. I don't think it's broswer caching related, as redirecting to the same view from a different controller works fine. How can I fix it ?
public ViewResult Index()
{
return View(GetComments());
}
[HttpPost]
public ActionResult Index(int[] AllIds)
{
if (AllIds != null)
{
foreach (int id in AllIds)
{
// do stuff
}
}
return RedirectToAction("Index");
}
Edit: When submitting the form, the breakpoint on the first method is not hit and trying to "Step Into (F11)" the return RedirectToAction("Index"); line just moves straight onto the final } instead.
Install Fiddler or Firebug for Firefox and watch the traffic, see it it really returns a new response or a HTTP 304 from the browser(cached page). If everything checks out then you have a problem with your db persistence and or queries.
Have you tried this? I'm wondering, depending on how you persist the data, if it's not being saved until after the server returns a response..?
public ViewResult Index()
{ // breakpoint
var comments = GetComments(); // debug and inspect the value of this variable
return View(comments);
}
[HttpPost]
public ActionResult Index(int[] AllIds)
{
if (AllIds != null)
{
foreach (int id in AllIds)
{
// do stuff
}
}
return RedirectToAction("Index"); // breakpoint
}
I know some people use an IUnitOfWork in MVC that only calls SaveChanges / Commit on the ORM at the end of the request. Is it possible that the // do stuff removes items from the collection, but does not persist to the db until AFTER the GET Index() is returned?
Update
Instead of return RedirectToAction("Index"), have you tried RedirectToAction(Index())?
Try entering controller name as well. That helped me. For example:
return RedirectToAction("Index","Home");
Okay so, i am totally new to MVC and I'm trying to wrap my head around a few of the concepts. I've created a small application...
This application has a view for creating a new Individual record. The view is bound to a model ViewPage... And I have a associated IndividualController which has a New method...
The New method of the IndividualController looks like this...
public ActionResult New()
{
var i = new Individual();
this.Title = "Create new individual...";
i.Id = Guid.NewGuid();
this.ViewData.Model = new Individual();
return View();
}
Now, the above all seems to be working. When the view loads I am able to retrieve the data from the Individual object. The issue comes into play when I try and save the data back through the controller...
In my IndividualController I also have a Save method which accepts an incoming parameter of type Individual. The method looks like...
public ActionResult Save(IndividualService.Individual Individual)
{
return RedirectToAction("New");
}
Now, on my view I wanted to use a standard html link/href to be used as the "Save" button so I defined an ActionLink like so...
<%=Html.ActionLink("Save", "Save") %>
Also, defined in my view I have created a single textbox to hold the first name as a test like so...
<% using (Html.BeginForm()) { %>
<%=Html.TextBox("FirstName", ViewData.Model.FirstName)%>
<% } %>
So, if I put a break point in the Save method and click the "Save" link in my view the break point is hit within my controller. The issue is that the input parameter of the Save method is null; even if I type a value into the first name textbox...
Obviously I am doing something completely wrong. Can someone set me straight...
Thanks in advance...
Your New controller method doesn't need to create an individual, you probably just want it to set the title and return the view, although you may need to do some authorization processing. Here's an example from one of my projects:
[AcceptVerbs( HttpVerbs.Get )]
[Authorization( Roles = "SuperUser, EditEvent, EditMasterEvent")]
public ActionResult New()
{
ViewData["Title"] = "New Event";
if (this.IsMasterEditAllowed())
{
ViewData["ShowNewMaster"] = "true";
}
return View();
}
Your Save action should take the inputs from the form and create a new model instance and persist it. My example is a little more complex than what I'd like to post here so I'll try and simplify it. Note that I'm using a FormCollection rather than using model binding, but you should be able to get that to work, too.
[AcceptVerbs( HttpVerbs.Post )]
[Authorization( Roles = "SuperUser, EditEvent, EditMasterEvent")]
public ActionResult Save( FormCollection form )
{
using (DataContext context = ...)
{
Event evt = new Event();
if (!TryUpdateModel( evt, new [] { "EventName", "CategoryID", ... }))
{
this.ModelState.AddModelError( "Could not update model..." );
return View("New"); // back to display errors...
}
context.InsertOnSubmit( evt );
context.SubmitChanges();
return RedirectToAction( "Show", "Event", new { id = evt.EventID } );
}
}
If I don't create a new Indvidual object in the New method then when my view tries to bind the textbox to the associated model I get a NullReferenceException on the below line in my view...
`<%=Html.TextBox("FirstName", ViewData.Model.FirstName)%>`
With regards to the Save method. From what I understand since my view is strongly typed shouldn't I be able to have a method signature like...
`public ActionResult New(IndividualService.Individual ind)
{
return View();
}`
I thought that was the purpose behind model binding..?
I would strongly recommend that you take a step back from what you are doing and run through some of the Tutorials/Videos here http://www.asp.net/learn/
If you have a strongly typed View it means that when the Controller picks that view to generate the output the view has better access to the Model.
However the View is not responsible for what comes back from the client subsequently such as when a form is posted or a URL is otherwise navigated to.
ASP.NET-MVC uses information in the URL to determine which Controller to hand the request off to. After that it's the controller's responsibility to resolve the various other elements in the request into instance(s) of Model classes.
The relationship between the incoming request and the controller is clouded by the significant assistance the ASP.NET-MVC routing gives the controller. For example a route can be defined to supply parameters to the controller method and thats all the controller needs and hence you don't see any code in the method relating to the http request. However it should be understood that the contoller method is simply processing a http request.
I hope you can see from the above that it would be too early in a requests life-cycle for an instance of a class from the model to passed to a public method on a controller. Its up to the controller to determine which model classes if any need instancing.
Using ASP.NET MVC there are situations (such as form submission) that may require a RedirectToAction.
One such situation is when you encounter validation errors after a form submission and need to redirect back to the form, but would like the URL to reflect the URL of the form, not the action page it submits to.
As I require the form to contain the originally POSTed data, for user convenience, as well as validation purposes, how can I pass the data through the RedirectToAction()? If I use the viewData parameter, my POST parameters will be changed to GET parameters.
The solution is to use the TempData property to store the desired Request components.
For instance:
public ActionResult Send()
{
TempData["form"] = Request.Form;
return this.RedirectToAction(a => a.Form());
}
Then in your "Form" action you can go:
public ActionResult Form()
{
/* Declare viewData etc. */
if (TempData["form"] != null)
{
/* Cast TempData["form"] to
System.Collections.Specialized.NameValueCollection
and use it */
}
return View("Form", viewData);
}
Keep in mind that TempData stores the form collection in session. If you don't like that behavior, you can implement the new ITempDataProvider interface and use some other mechanism for storing temp data. I wouldn't do that unless you know for a fact (via measurement and profiling) that the use of Session state is hurting you.
Take a look at MVCContrib, you can do this:
using MvcContrib.Filters;
[ModelStateToTempData]
public class MyController : Controller {
//
...
}
There is another way which avoids tempdata. The pattern I like involves creating 1 action for both the original render and re-render of the invalid form. It goes something like this:
var form = new FooForm();
if (request.UrlReferrer == request.Url)
{
// Fill form with previous request's data
}
if (Request.IsPost())
{
if (!form.IsValid)
{
ViewData["ValidationErrors"] = ...
} else {
// update model
model.something = foo.something;
// handoff to post update action
return RedirectToAction("ModelUpdated", ... etc);
}
}
// By default render 1 view until form is a valid post
ViewData["Form"] = form;
return View();
That's the pattern more or less. A little pseudoy. With this you can create 1 view to handle rendering the form, re-displaying the values (since the form will be filled with previous values), and showing error messages.
When the posting to this action, if its valid it transfers control over to another action.
I'm trying to make this pattern easy in the .net validation framework as we build out support for MVC.
If you want to pass data to the redirected action, the method you could use is:
return RedirectToAction("ModelUpdated", new {id = 1});
// The definition of the action method like public ActionResult ModelUpdated(int id);
TempData is the solution which keeps the data from action to action.
Employee employee = new Employee
{
EmpID = "121",
EmpFirstName = "Imran",
EmpLastName = "Ghani"
};
TempData["Employee"] = employee;