Using Custom paths or absolute URI with Html.BeginForm() [duplicate] - asp.net-mvc

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Html.BeginForm() with an absolute URL?
How to put and absolute URI in the action attribute of the <form> tag generated by the Html.BeginForm() MVC3 Html helper. I am using MVC3 validation and do not want to lose it by writing my own Form tags.
I tried out both Html.BeginForm(new { action ="http://absolute.com"}) and Html.BeginForm(new { #action ="http://absolute.com"}) and the rendered html was <form action="/Pro/Contact/http%3a/absolute.com/">. As you can see it is being appended instead of replaced.

BeginForm method with single parameter is used to generate a relative path from routedata that is provided within parameter variable. Which means, that you are setting URI parameter with name action and giving it and value http://absolute.com, therefor value is going to be encoded.
What you want to use is overload where it asks you for htmlAttributes.
This will not encode value for action attribute:
#using (Html.BeginForm(
null, null, FormMethod.Post,
new {#action="http://absolute.com/submit/example"}
)){}
// the result is:
<form action="http://absolute.com/submit/example" method="post">
</form>
UPDATE: method show below will not utilize JavaScript client-side validation.
since, you don't really need the helper to figure out the path of the form. You can use the html and set action of the form manually.
Validation will still work, if you are using helpers for the input fields.
<form action="http://absolute.com/submit/example" method="post">
#Html.LabelFor(model => model.Example)
#Html.ValidationMessageFor(model => model.Example, "*")
#Html.TextAreaFor(model => model.Example, 8, 60, null)
</form>

One way is..we can use RedirectResult("http://absolute.com"); in the controller action method.
If you are redirecting from your controller (or action filter, etc.) you can use the RedirectResult as your ActionResult type.

Related

MVC Core form tag helper that inherit the current view URL for its action attribute

With MVC5 (and older MVC frameworks), I was able to write my form without specifying the controller/action method:
#using (Html.BeginForm())
{
}
This way, I was able to reuse the form with different URL. If the form was called from the route "/books/2/edit", the HTML generated would be:
<form action="/books/2/edit"></form>
And if I call the form using the URL "/books/add", the HTML generated would be:
<form action="/books/add"></form>
How can I do the same with the tag helper syntax? I have tried all kind of syntaxes but it always generate an empty action attribute:
<form></form>
<form asp-route=""></form>
<form asp-controller="" asp-action=""></form>
Result:
<form></form>
<form action></form>
<form action></form>
When using HTML helpers, values that are not supplied explicitly default to the route values that are in the current request. That is the reason why you can specify BeginForm with no parameters.
When using tag helpers, this default logic no longer applies - the values must be provided explicitly. There are no defaults.
Option 1 - form tag
The simplest way to mimic what the HTML helper does with a form tag is:
<form action="#Url.RouteUrl(this.ViewContext.RouteData.Values)" method="post">
</form>
Option 2 - Html.BeginForm
Note that your current syntax is also still valid in ASP.NET Core MVC:
#using (Html.BeginForm())
{
}
But since you had to ask this question, I would say that it is absolutely not clear how the URL is being generated when using this syntax, which means you should probably change to using Url.RouteUrl to make it more readable despite being a bit more to write.
Option 3 - Tag Helper
Here is an example of how you could use a tag helper to achieve this, although it is a bit ugly.
There is a form tag helper attribute asp-all-route-values that allows you to pass all of the route values in a single parameter. However, according to asp-all-route-data must be IDictionary<string,object> or RouteValueDictionary, it is not possible to pass a RouteValueDictionary to this attribute, you would need to convert it to an IDictionary<string, string>. One way to do that is to build an extension method to make the conversion.
public static class RouteValueDictionaryExtensions
{
public static IDictionary<string, string> ToTagHelperValues(this RouteValueDictionary routeValueDictionary)
{
var result = new Dictionary<string, string>();
foreach (var kvp in routeValueDictionary)
{
result.Add(kvp.Key, kvp.Value as string);
}
return result;
}
}
You can then use a tag helper to generate the current URL as follows:
<form asp-all-route-data="#this.ViewContext.RouteData.Values.ToTagHelperValues()">
</form>
Option 4 - No action attribute
It is also possible to use a form tag with no action attribute. If you omit the action attribute, the default behavior in most (if not all) browsers is to use the current URL.
<form method="post">
</form>
WARNING: It is not standards compliant to use this option and technically the behavior of browsers do not have to default to the expected behavior of using the current URL if it is not supplied.
In the end, which method you use is a matter of preference.

mvc maintain url parameters during post

I currently have a form setup on the following url:
http://localhost/mySite/inventory/create/26/1
this goes to an actionMethod
[HttpGet]
public ActionResult create(int exId, int secId)
[HttpPost]
public ActionResult create(MyModel model, int exId, int secId, FormCollection form)
the form submit and button look like:
#using (Html.BeginForm("create", "inventory", new { #exId= Model.ExId, #secId= Model.SecId}, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
and
<div class="col-md-6">
<input type="submit" class="btn blue" value="Search" name="Searchbtn" id="Searchbtn" />
</div>
my question was is there anyway during the post to keep the url as: /create/26/1, right now when the post occurs the url is changed to:
http://localhost/mySite/inventory/create?exId=26&secId=1
is there anyway to keep it as how the get has it, /create/26/1?
That's most likely a routing issue. MVC short-circuits routing. In other words, as soon as it finds something that will work, it uses that, even if there is perhaps a better route available. In your scenario here, it's finding that /inventory/create is a valid route, and since query string params are arbitrary, it's just sticking the rest of the route values there. It's hard to say without seeing your route config, but if the route you have for /inventory/create/{exId}/{secId} comes after whatever route catches just /inventory/create, you should move it before that. If you're using attribute routing, there's no inherent order or routes, so you'll have to use a route name to distinguish which one you actually want to use.
All that said, the easiest thing to do here is to simply not generate the URL. You're doing a postback, so you can just use an empty action. I think you're mostly having this issue because you're trying to pass htmlAttributes to Html.BeginForm, which then requires that you specify a bunch of extra stuff that's not necessary. In these situations I recommend just using a static <form> tag. You don't have to use Html.BeginForm and when you find yourself doing contortions like this, it's better to just not.
<form class="form-horizontal" role="form" action="" method="post">
...
</form>

how can I understand where this "submit" button/actionlink leads in ASP MVC 4?

I am new to ASP.NET MVC 4 and learning my way around an existing MVC 4 code base. I am trying to find the code that processes the the form corresponding to this submit button. I understand that the action link probably says how to process the "submit" button -- but I don't see any constructors that take three strings for an actionlink in the microsoft documentation.
I am confused because there is no action field in the input tag.
How do I find out what happens once the person hits submit?
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data", #class = "disableSubmit" }))
{
...
<div class="buttons">
<input type="submit" value="Upload" /> | #Html.ActionLink("Back to List", "Index", "Admin")
</div>
}
Update When I go to "view source" to see the raw HTML I see
<form action="/Lab/Upload" ...
So that means it goes to the lab/upload controller?
The Javascript for the disable submit looks like this:
// Disable submit button after click
$(function () {
$('.disableSubmit').submit(function () {
if ($(this).valid()) {
$('input[type="submit"]').attr('disabled', 'disabled');
}
});
});
If the submit button performs a regular form submit, then it will be inside of a <form> tag or #Html.BeginForm using block.
BeginForm will submit the form to the action that matches the name of the view, unless there is a parameter being passed that specifies the action name and/or controller name.
IF it is a form tag, then the action="something" attribute of the form tag will indicate the URL being submitted to, which is usually controllerName/ActionName` but could be different depending on what routing is setup.
The ActionLink you see is not related to the form or the submit, it is a regular link which is in effect a way for the user to go back to the previous page instead of submitting the form.
There is also the possibility that there is javascript attached to the submit button. That's harder to find unfortunately due to the many ways that javascript can be wired up to a button.
Edit: Based on your update, I would strongly suspect there's javascript that supports this form. I imagine the submit button is disabled until you meet some conditions that allow it to be displayed. Maybe permissions, maybe filling the form out completely, it's hard to say. Search the javascript for disableSubmit, as I suspect somewhere there is code that removes that class under certain conditions.
Edit 2: What is happening there is it disables the submit button after the first click so that you can't accidentally submit the for twice and cause problems with a duplicate submit(if this is Create form it avoids duplicate records). As far as I can tell there should be an action of the same name as the *.cshtml file that it submits to. Possibly with a [Post] or [HttpPost] attribute on the action.
Check whether the submit buttons in inside a form tag. If yes, what is the action attribute value of that ? That is the place the form will be submitted to.
You may see an HTML helper method called Html.Beginform in the view. This method render a form tag. You can go to the page and check the view source of the page to see what is the form tag looks like and what is the action method attribute value.
Ususally your controller will have an action method marked with HttpPost attribute to handle the form submit. Lets say your mark up is like this
<form action="User/EditUser">
<input type="text" name=Name" />
<input type="submit" />
</form>
Now in your UserController, there may be an action method like this
[HttpPost]
public ActionResult EditUser(SomeModelIfExist model)
{
// TO DO : save and redirect
}
the Html.ActionLink helper method renders an anchor tag. It has nothing to do with the form submit. So your action link helper will return the below markup
Back

How does #Html.BeginForm() works?

I'm very new to ASP.NET, just started the MVC tutorial today on asp.net. I got here http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/examining-the-edit-methods-and-edit-view
So far so good, the problem:
In my View I have the following code
(Model is set to the view with #model MyFirstMVC4.Models.Movie)
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Movie</legend>
#Html.HiddenFor(model => model.ID)
//... bla bla html input
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
My MovieController
// Shows the view
public ActionResult Edit(int id = 0)
{
Movie movie = db.Movies.Find(id);
if (movie == null)
{
return HttpNotFound();
}
return View(movie);
}
//
// POST: /Movie/Edit/5
[HttpPost] // Handles the view above
public ActionResult Edit(Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
And here is the question - How the heck does it pass the Movie object to the POST method above?! When I observe the client side there is
<form action = "/Movie/Edit/1" ... />
Here I don't understand why action = url of the very same view page?!1
Also on the server side there is just Html.BeginForm() :(
How does it realize to what action method to post and what route parameters to pass?
It works, I just don't know why
The version of BeginForm in the code,
with no parameters, sends an HTTP POST to the current URL, so if the view is a response to
/Movie/Edit/5, the opening form tag will look like the following:
< form action="/Movie/Edit/5" method="post">
The BeginForm HTML helper asks the routing engine how to reach the Edit action of the
MovieController. Behind the scenes it uses the method named GetVirtualPath on the Routes
property exposed by RouteTable — that’s where your web application registered all its routes in
global.asax. If you did all this without an HTML helper, you’d have to write all the following
code:
#{
var context = this.ViewContext.RequestContext;
var values = new RouteValueDictionary{
{ "controller", "movie" }, { "action", "edit" }
};
var path = RouteTable.Routes.GetVirtualPath(context, values);
}
<form action="#path.VirtualPath" method="get">
...
</form>
You asked how is movie object is passed. That is called model binding.
When you have an action with a parameter, the MVC runtime uses a model binder to build the
parameter. You can have multiple model binders registered in the MVC runtime for different types
of models, but the workhorse by default will be the DefaultModelBinder.
In the case of an Movie
object, the default model binder inspects the Movie and finds all the movie properties available
for binding. Following the naming convention you examined earlier, the default model binder can automatically convert and move values from the request into an movie object (the model binder can
also create an instance of the object to populate).
In other words, when the model binder sees an Movie has a Title property, it looks for a value
named “Title” in the request. Notice the model binder looks “in the request” and not “in the form
collection.” The model binder uses components known as value providers to search for values in
different areas of a request.
The model binder can look at route data, the query string, and the form
collection, and you can add custom value providers if you so desire.
When you call BeginForm() without any parameters it default to using the same controller/action used to render the current page. It assumes you'll have an action with the correct name on your controller that will accept postbacks (which you do). It uses the RouteValues to do this.
It automatically binds each input control (by name) to the parameters of the action accepting the postback - or in your case, the properties of the object parameter for the action accepting the postback.
[HttpPost] attribute is given to the action that you want to be called on the POST submit of the form.
to understand how #using (Html.BeginForm()) works , you need to know what page it is already on . using #using (Html.BeginForm()) in 2 different views will come back to two different controllers
We can create forms by typing simple html or by html helpers.
One of them Html.BeginForm(); it is a little bit odd because you actually can wrap it in a using statement because this particular helper returns an object that implements IDisposable in C#. First it writes out with opening tag. And at the bottom when the generated code calls dispose on that object, that’s when it will write out closing form tag . So BeginForm gives me an object that will write out my opening form tag and my closing from tag. After that you don't worry about anything you can just focus on labels and inputs

if no route added form action is is not created why?

I Have this in a view in an area
<form action='#Url.Action("/DeleteCoverage")'></form>
but this gives only the following html
<form action=''></form>
but the following code
#using (Html.BeginForm("DeleteCoverage", "Coverage", new { area = "Coverage" }, FormMethod.Post, new { id = "delform" }))
gives html
<form action="/Coverage/DeleteCoverage/af361feb-1818-430b-803c-e332a162b0e2" id="delform" method="post">
I get this HTML only if i add a route for this method. Other wise it just becomes action=""
Why i am not getting proper HTML if I don't add route?
You are incorrectly using the Url.Action helper. This helper has several overloads and expects you to pass action, controller, route values, ... Currently you are passing /DeleteCoverage which is invalid action name (action names cannot start with /).
So pick one of the overloads and use it correctly:
<form action="#Url.Action("DeleteCoverage", "Coverage", new { area = "Coverage" })"></form>
Oh and by the way you should probably use Html.BeginForm to generate your form tags instead of hardcoding them.

Resources