MVC typing URL doesn't work, links do - asp.net-mvc

I have an MVC site. I have a form submit (it submits no data in this case) that triggers an Action in the Controller and returns a View. If I refresh, it asks to resubmit the form and works. If I click on the URL in the "awesome bar" or whatever, and hit Enter, I get a 404 resource cannot be found error.
The URL displayed is /pts/reports/subject. I don't get what's happening. Any suggestions?
Action in Controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Subject()
{
return View(new SubjectModel());
}
Form:
<% using (Html.BeginForm("Subject", "Reports", FormMethod.Post, new {#class = "form-a"}))
{ %>
<fieldset>
<h5 class="section-header">Subject</h5>
<div class="positionbottom">
<button type="submit" name="Submit" value="csv" class="right">Web</button>
</div>
</fieldset>
<% } %>
MapRoute:
context.MapRoute(
"Pts_Reports",
"pts/reports/{action}/{id}",
new { controller = "Reports", action = "Index", id = UrlParameter.Optional }
);

The action expects a POST:
[AcceptVerbs(HttpVerbs.Post)]
If you submit a form with a POST method (which you are... FormMethod.Post), then it will work as expected. Which you've observed. However, just clicking a link or manually entering the address is a GET request, not a POST request. So there's no matching action. Thus, a 404 error.
In order you accept any request, just remove that AcceptVerbs usage. But keep in mind that GET requests won't contain form data. The action you show in the code doesn't use any form data, so that's fine. (But then why are you posting a form to it?)

Related

Submitting form with Html.ActionLink submit inside an Ajax Form

I have an AJAX.BeginForm() form and I want to submit an Html post as the final post. Here's my form
#using (Ajax.BeginForm("createSet", "Workout",
new AjaxOptions { InsertionMode = InsertionMode.Replace, UpdateTargetId = "divs",
HttpMethod = "POST"}, new { id = "myForm" }))
{
<div id="divs">
#Html.Partial("WorkoutSet", Session["WorkoutSetList"])
</div>
#Html.ActionLink("Submit", "SavePlan", "Workout", new { type = "submit" });
//<input type="submit" value="Submit" id="submit" name="command" />
<input type="submit" value="Create Set" name="command" />
}
Basically my "create set" button is used a lot for ajax calls which is working fine. I'm able to bind the model rendered from the partial view and update my model to session on every ajax call.
For my final step, I want to submit the whole form to the SavePlan Action along with the model values and then redirect to home page after that.
Problems faced:
First I tried the Ajax Submit button(the code commented out), a script redirects it to the SavePlan along with the model values rendered from partialview. Only problem is I could not redirect to HomePage and I've read that redirection cannot be done on an ajax call.
Second, I've tried the #Html.ActionLink which does goes to the SavePlan action and then proceeds to redirect to HomePage, BUT the model values were never passed. I've tried FormCollection but the values don't get passed either.
Please provide some advise on how to solve this? I've already spent 2 days on it and I'm tearing myself on it.
To recap:
Trying the Ajax way allows me to pass the model values but does not redirect to home page
Trying the Html way doesn't allow me to pass the model values but does redirect to home page.
You can redirect from an ajax POST - in your controller, you need to return a window.location javascript snippet:
return JavaScript(string.format("window.location = '{0}'", Url.Action("Index")));
There is an article here: Breaking out of an AJAX POST

MVC4 Razor Looses UserID if Unbound

I'm creating some user profile edit forms in MVC4 at the moment and for testing I was rendering the UserId property into a readonly textbox on the form like this:
<li>
#Html.LabelFor(model => model.UserId)
#Html.TextBoxFor(model => model.UserId, new { #readonly="readonly"})
</li>
As I'm nearing completion of the edit form I removed this textbox as it's just using up real estate. Once I had done this the model sent back to the controller when saving had the integer default value of 0 and then the Entity Framework blows up as it cannot update any rows. So I added this to the form:
<li>
#Html.HiddenFor(model => model.UserId, new { #readonly="readonly"})
</li>
Is this a safe move? Should I be using the ViewBag for things like this? On the profile details page I render an edit button like this:
#Html.ActionLink("Edit", "Edit", new { id=Model.UserId })
Meaning that the UserId is rendered in the link. Is this safe and secure or do I need to rethink how I move the models and ids around the UI?
TIA,
Is this a safe move?
This will do the job of sending the id to the server. Just get rid of the readonly="readonly" attribute which makes very little sense for a hidden input.
Should I be using the ViewBag for things like this?
This doesn't change anything in terms of security. Any user could still put whatever id he wants. Whether you are using a hidden field or an ActionLink you are still sending the id as plain text to the server and anyone could forge a request and put whatever id he wants. So if you site uses some form of authentication you must absolutely check on the server side that the id that you received actually is a resource that belongs to the currently authenticated user before attempting to perform any actions on it. Otherwise some authenticated user could supply the id of a resource that belongs to another user and be able to update it. Of course that's just a hypothetical scenario, it's not clear at all if this is your case and whether this id needs to be secured.
If UserId is sensitive, then there are other options
Keep UserId server side only with Session state (if your architecture allows for Session)
Put it in an encrypted cookie. Note as per Darin, that these can be compromised.
If it isn't sensitive, then your HiddenFor is fine - post it back with the rest of the form.
Don't put it in your ActionLink Querystring unless this is part of your route (i.e. /Controller/Action/id)
I would strongly suggest using ValueInjecter. Here is a code snippet doing the same thing
[HttpGet]
public new ActionResult Profile()
{
var model = new ProfileModel();
model.InjectFrom<UnflatLoopValueInjection>(this.GetCurrentUser());
return View(model);
}
[HttpPost]
public new ActionResult Profile(ProfileModel model)
{
if (ModelState.IsValid)
{
this.GetCurrentUser().InjectFrom<UnflatLoopValueInjection>(model);
try
{
_userService.SaveOrUpdate(this.GetCurrentUser());
TempData["Success"] = "User was successfully updated.";
return RedirectToAction("Profile");
}
catch (Exception)
{
ModelState.AddModelError("Exception", "Unexpected error");
}
}
return View(model);
}
And here is the view...
#using (Html.BeginForm("Profile", "Account", FormMethod.Post, new { #class = "form-horizontal" }))
{
#Html.ValidationSummary(true, "Unable to update profile. Please correct the errors and try again.", new { #class = "alert alert-block alert-error" })
#Html.EditorForModel()
<div class="form-actions">
<input type="submit" value="Update" class="btn btn-primary" />
</div>
}

Can't get RedirectToaction to show completely new page using Ajax.BeginForm in MVC

Hoping someone can help me solve this issue.
I'm using Ajax.BeginForm quite often when I need to update a part of my actual webpage. But if I have a second button in the web form where I need go to a complete different page, and for example do a search and get some values that I need to complete the form. The page turns up in the update panel (updateTargetID) instead of in a complete new View. That is happening even id a do a RedirectToAction in the controller. Of course the ajax.beginform does what I accept it to do, in other words update the panel that shall be updated. But is there a way to get ajax.Beginform to use redirectToaction without updating when the user is choosing to do for example a search instead of sending the form?
I'm working with asp.net MVC,
Problem;
<% using (Ajax.BeginForm(new AjaxOptions() { LoadingElementId = "loadingElement", UpdateTargetId = "SearchResult", InsertionMode = InsertionMode.Replace}))
{%>
<% Html.RenderPartial("Searchfields", Model); %>
<div>
<%:Html.ActionLink("blank", "Index")%>
</div>
<div id="loadingElement" style="display: none">
Load data...
</div>
<div id="SearchResult">
<% if (Model.SearchResult != null)
{
Html.RenderPartial("SearchResult", Model.SearchResult);
} %>
</div>
<% } %>
In the controller (post) I do this among other stuff;
if (Request.IsAjaxRequest())
{
return PartialView("SearchResult", data.SearchResult);
}
But before this I need to do:
if (data.SearchResult.Count() == 1)
{
return RedirectToAction("Edit", "Person", new { Id = data.SearchResult.First).Id});
}
but ofcourse if I do that the hole page i rendered in the corresponding updateTargetId,
I need to render a new view instead. So how can I for exampel use javascript to redirect oncomplete and have the values from the model sent to the script to for exampel do a window.location.replace?
I'm struggling with javascript, so please help me!

Reusable components in ASP.NET MVC

I have feature on my website (some UI and associated functionality) that I want to be able to reuse on multiple pages. For the purposes of this question, let's say it's a "Comments" feature.
There is an area in my application for Components and within the area are a controller: /Controllers/CommentController, and two partial views: /Views/Comment/Index.ascx (for listing comments) and /Views/Comment/Create.ascx (for creating comments).
CommentController looks something like this:
public class CommentController : Controller
{
[ChildActionOnly]
public ActionResult Index()
{
return PartialView(GetComments());
}
[HttpGet]
[ChildActionOnly]
public ActionResult Create()
{
return PartialView(); //this is wrong.
}
[HttpPost]
[ChildActionOnly]
public ActionResult Create(FormCollection formValues)
{
SaveComment(formValues);
return RedirectToAction("Index"); //this is wrong too.
}
}
Index Partial View:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<div>
<% foreach (var item in Model) { %>
<div>
<%: item.Comment %>
</div>
<% } %>
<%: Html.ActionLink("Add a Comment", "Create", "Comment", new { area = "Components" }, null) %>
</div>
Create Partial View:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<div>
<% using (Html.BeginForm())
{%>
Enter your comment:
<div>
<input type="text" name="comment" />
</div>
<p>
<input type="submit" value="Create" />
<% //also render a cancel button and redirect to "Index" view %>
</p>
<% } %>
</div>
The Index partial view is included in a view with RenderAction, like so:
<% Html.RenderAction("Index", "Comment", new { area = "Components" }); %>
This code doesn't work because the forms within the partial views submit to actions on the CommentsController that are marked [ChildActionOnly] (this is by design, I don't want the "Components" to be requested independently of a hosting page).
How can I make this "component" approach work, i.e. have a partial view that submits a form to change the state of a component within a page without losing the hosting page itself?
EDIT:
To clarify, the use of [ChildActionOnly] is not my problem here. If I remove the attribute from my action methods, my code only "works" in that it doesn't throw an exception. My "component" still breaks out of its hosting page when its form is submitted (because I'm telling the form to submit to the partial view's URL!).
You are making MVC fight itself by asking a form to target an action that is marked as ChildActionOnly.
My solution to this problem when I was designing a highly reusable wizard framework, was to NOT mark the actions as ChildActionOnly but instead to detect if the request was an ajax one or just a plain vanilla request.
The code for all this is packaged into a base controller class. In your derived controllers, you do something like:
[WizardStep(4, "Illness Details")]
public ActionResult IllnessDetails()
{
return Navigate();
}
Where the Navigate() method of the base controller has decided whether to return the full view or just the partial view, depending on whether it is, or isn't, an ajax request. That way, you can never return the partial view in isolation.
To ascertain if it is an Ajax request, I used a combination of Request.IsAjaxRequest() and TempData. The TempData is needed because my wizard framework implements the PRG pattern out of the box, so I need to persist the fact that the original post was an ajax one.
I guess this is just one solution and it took a bit of trial and error to get it right. But now I live happily ever after developing wizards like I was JK Rowling...
Use Ajax to post the partial.

ASP.NET MVC form that redirects to a route

Hello is it possible to have an ASP.NET MVC form that uses the routes defined in Global.asax to post its values (via a GET request)? I have the form defined like this:
<% using (Html.BeginForm("CanviaOpcions","Sat",FormMethod.Get))
{ %>
<fieldset>
<legend>Opciones</legend>
<%= Html.DropDownList("nomSat")%>
<input type="submit" />
</fieldset>
<% } %>
and the following route in my global.asax:
routes.MapRoute(
"Canvia Opcions",
"Sat/{nomSat}",
new { controller = "Sat", action = "CanviaOpcions" }
);
I want that after a submit the form with nomSat having the value XXX to have the following URL in my browser: http://machinename/sat/XXX
Is it possible?
No, you can't add to the routing parameters using an HTML form.
You can simulate the behaviour with a Javascript function though. Like this :
<fieldset>
<legend>Opciones</legend>
<%= Html.DropDownList("nomSat")%>
<input type="button"
onclick="window.location=('/<%=Url.Action("CanviaOpcions", "Sat") %>/' +
$('#nomSat').val())" />
</fieldset>
Do you really care about the URL you navigate to, or do you only care about what the next URL the user sees is?
If you just care about the URL the user sees, then you don't need to use the method you are trying.
What you could do is have a post action that reads in the "nomsat" parameter, then redirect to another action that has the URL you are wanting.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(string nomsat)
{
...
return RedirectToAction("Detail", new RouteValueDictionary {{"nomsat", nomsat}});
}
public ActionResult Detail(string nomsat)
{
...
return View();
}

Resources