Reputation: 18220
Greetings.
In attempt to wrap my head around MVC I've just implemented a simple add/delete page which views records as well. This makes it easier for users to do most things from one page instead of having to navigate away to do simple tasks. The page lets you add a record (a business which has an ID and a name) which works fine. However when I delete a record I've done the following to allow deletions to occur:
<%= Html.ActionLink("delete", "DeleteBusiness", new { businessToDelete = B.BusinessID }) %>
This works fine to delete the record. This is what my controller action looks like for it:
public ActionResult DeleteBusiness(string businessToDelete)
{
try
{
if (!ModelState.IsValid)
return View("Businesses", _contractsControlRepository.ListBusinesses());
_contractsControlRepository.DeleteBusiness(businessToDelete);
return View("Businesses", _contractsControlRepository.ListBusinesses());
}
catch
{
return View("Businesses", _contractsControlRepository.ListBusinesses());
}
}
So from the Businesses page I have an ActionLink which just directs me to this action in the controller which does the work and then returns the view that I was previously at. Then problem is that once I've deleted the record my actual URL ends up like this:
http://localhost:3340/Accounts/ContractsControl/DeleteBusiness?businessToDelete=TEST
This isn't good, because when I then go to add a new record, it won't let me. My controller action which lets me add records looks like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Businesses(Business business)
{
try
{
if (!ModelState.IsValid)
return View(_contractsControlRepository.ListBusinesses());
_contractsControlRepository.CreateBusiness(business);
return View(_contractsControlRepository.ListBusinesses());
}
catch
{
return View(_contractsControlRepository.ListBusinesses());
}
}
Any post
request is seen as creating new records. Is this correct? Here's my view just to top it all off:
<% using (Html.BeginForm())
{ %>
<fieldset class="inline-fieldset">
<legend>Add Business</legend>
<p>
<label for="ID">ID</label>
<%= Html.TextBox("BusinessID", null, new { style = "width:50px;", @class = "uppercase", maxlength = "4" })%>
<%= Html.ValidationMessage("BusinessID", "*")%>
<label for="Business">Business</label>
<%= Html.TextBox("BusinessCol")%>
<%= Html.ValidationMessage("BusinessCol", "*")%>
<input type="submit" value="Add" />
</p>
<%= Html.ValidationSummary("Uh-oh!") %>
</fieldset>
<% } %>
<table>
<tr>
<th>ID</th>
<th>Business</th>
<th></th>
<th></th>
</tr>
<% foreach (Business B in ViewData.Model)
{ %>
<tr>
<td><%= B.BusinessID %></td>
<td><%= B.BusinessCol %></td>
<td class="edit"><%= Html.ActionLink("edit", "EditBusiness", new { id = B.BusinessID }) %></td>
<td class="delete"><%= Html.ActionLink("delete", "DeleteBusiness", new { businessToDelete = B.BusinessID }) %></td>
</tr>
<% } %>
</table>
As you can see at the very bottom I have the action links (ignore the edit one). So if I delete a record, then immediately try to add one, it won't work because (I'm assuming) of the URL being wrong. What am I doing wrong here?
FIXED
public RedirectToRouteResult DeleteBusiness(string businessToDelete)
{
try
{
if (!ModelState.IsValid)
return RedirectToAction("Businesses");
_contractsControlRepository.DeleteBusiness(businessToDelete);
return RedirectToAction("Businesses");
}
catch
{
return RedirectToAction("Businesses");
}
}
Upvotes: 2
Views: 1687
Reputation: 8352
I've just gone through the NerdDinner tutorial.
In there the controllers for Delete are
// GET: /Dinners/Delete/1
public ActionResult Delete(int id)
{
Dinner dinner = dinnerRepository.GetDinner(id);
if (dinner == null)
{
return View("NotFound");
}
else
{
return View(dinner);
}
}
// POST: /Dinners/Delete/1
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Delete(int id, string confirmButton)
{
Dinner dinner = dinnerRepository.GetDinner(id);
if (dinner == null)
{
return View("NotFound");
}
dinnerRepository.Delete(dinner);
dinnerRepository.Save();
return View("Deleted");
}
and the controllers for Get are.
// GET: /Dinners/Edit/2
[Authorize]
public ActionResult Edit(int id)
{
Dinner dinner = dinnerRepository.GetDinner(id);
if (!dinner.IsHostedBy(User.Identity.Name))
{
return View("InvalidOwner");
}
return View(new DinnerFormViewModel(dinner));
}
//
// POST: /Dinners/Edit/2
[AcceptVerbs(HttpVerbs.Post),Authorize]
public ActionResult Edit(int id, FormCollection formValues)
{
Dinner dinner = dinnerRepository.GetDinner(id);
if (!dinner.IsHostedBy(User.Identity.Name))
{
return View("InvalidOwner");
}
try
{
UpdateModel(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id = dinner.DinnerID });
}
catch (Exception ex)
{
foreach (var issue in dinner.GetRuleViolations())
{
ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
}
return View(new DinnerFormViewModel(dinner));
}
}
Upvotes: 1
Reputation: 14229
I second maciejkow's answer, redirecting eliminates the chance of the user re-posting the data if they try refresh the page.
Another thing, I wouldn't put delete actions in a link (i.e. a GET), these sorts of actions should always be POSTs (automated tools can sometimes follow links on a page for caching reasons), so I would wrap a button in a mini-form to do your delete, with your businessToDelete variable in a hidden field.
Upvotes: 1
Reputation: 6453
Why don't you redirect user back do List Businesses page if delete succeded?
Upvotes: 4