BeginForm with null controller and action missing parameters - asp.net-mvc

I want to add an ID attribute to my form, so I follow the answer here
Originally my code was using:
Html.BeginForm()
and the output looks like this:
<form action="/Controller/Action?Id=5" method="post">
but when I replace the Html.BeginForm() with:
Html.BeginForm(null, null, FormMethod.Post, new { #Id = "blah" });
the output form is missing the parameter:
<form action="/Controller/Action" method="post">
It all seems to just work "magically" if I replace the BeginForm with <form action="" method="post"> but I'd like to understand what's going wrong with the helper version.

You're trying to use this version of the method, correct?
<ExtensionAttribute> _
Public Shared Function BeginForm ( _
htmlHelper As HtmlHelper, _
actionName As String, _
controllerName As String, _
method As FormMethod, _
htmlAttributes As Object _
) As MvcForm
Couple of things:
htmlAttributes is the attributes of your form tag, and has nothing to do with the ?Id=5 part of your query string.
Why is your "Id" variable prefixed with an '#' symbol anyway? But again, because of #1 above, it doesn't matter. You don't need to use this.
I'm guessing that Html.BeginForm() "just works" because you are viewing this as the result of a GET request on /Controller/Action/5. The default POST, then, would have the same URL.
The same goes if you do -- in HTML, when you specify the empty string ("") for the URI in the action parameter, it will interpret that as the current URI. See RFC 2396:
4.2. Same-document References
A URI reference that does not contain a URI is a reference to the
current document. In other words, an empty URI reference within a
document is interpreted as a reference to the start of that document,
and a reference containing only a fragment identifier is a reference
to the identified fragment of that document. Traversal of such a
reference should not result in an additional retrieval action.
However, if the URI reference occurs in a context that is always
intended to result in a new request, as in the case of HTML's FORM
element, then an empty URI reference represents the base URI of the
current document and should be replaced by that URI when transformed
into a request.
Does this answer your questions?
Edit: Ah, I see what you're asking now. I'm not sure why passing null doesn't include the same URI. If you explicitly include the action name and controller name, does it work? I've done it this way in MVC applications and it has worked properly.

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.

In MVC Razor, how can I correctly add querystring parameters to an html.actionlink?

I have an MVC Razor page where I am trying to add a querystring parameter to the URL.
<td>
#Html.ActionLink(item.Student_Name, "Index", "FourCourseAuditDetails",
new { filterByStudent = item.Student_Name})
</td>
My desired outcome is: http://[server]/FourCourseAuditDetails/Index?filterByStudent="[item.Student_Name]", but when I test out the page, the anchor href attirubte looks like this: http://[server]/[route of current view]?length=[integer value]
According to the Intellsense and the Html.ActionLink descriptions, I am using the correct arguments, but clearly something is not right.
The thing that came to mind is that item.Student_Name is a string that contains spaces and commas. I tried filterByStudent = item.Student_Name.toString(), which didn't help. I still think that's the cause of the problem, but I don't know what else I can do. Any ideas?
The problem arise from the fact that there is no overload as LinkExtensions.ActionLink Method (HtmlHelper, String, String, String, Object)
Use the overloaded method LinkExtensions.ActionLink Method (HtmlHelper, String, String, String, Object, Object)
#Html.ActionLink((string)item.Student_Name, "Index", "FourCourseAuditDetails",
new { filterByStudent = item.Student_Name}, null)
Also need to type cast item.Student_Name to string.

Passing String Array parameter to the MVC Controller

I have the following controller to take four parameter and one to get an array of string.
public Response Get(string a, double b, double c, string[] d)
{
do something
}
As u see the fourth(d) parameter is an array of strings. The first 3 parameter get the value but d shows null.
I am using fiddler to debug with following url
04/api/Controller?a=Hello&b=-37.8031231&c=144.9836514&d=Italian&d=bars
What m i doing wrong? is it the url?
Without seeing your View, I can abstract that you named 2 inputs as name='d' without indexing
example:
You can create an array by naming your id inputs with array indexes.
#using (Html.BeginForm())
{
<input type="text" name="d[1]">
<input type="text" name="d[2]">
}
If you index your inputs, the framework will use them as a List
Note:
you bring up URL. You cannot pass an array or any complex object without a Post.
You can post array data from fiddler, the URL you have provided is correct. But make sure you have the correct content type in request header.
you don't have to create view to test your controller action method.
Client request
Server respone
P.S. Above provided is for controller action method, I have noticed you are using api controller after posting the answer. I will edit the answer once done for api controller.
The Above works for APIController as well, If you are posting from fiddler

Using Custom paths or absolute URI with Html.BeginForm() [duplicate]

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.

Using a named route in Form.Begin in ASP.NET MVC

Is there a way to force use of a named route in ASP.NET MVC when using Form.Begin. I'm learning more and more about routing and getting scared that it can be very fragile if you just change the order or whether or not parameters have defaults.
<% Form.Begin(...) %> <!-- no overload for providing a route name --%>
There does not seem to be an overload for a named route for beginning a form so instead the best I could come up with was this :
<form action="/Products/Command/ViewProduct" method="post">
I'm wondering if this missing overload is an oversight (Beta right now), if there is a reason for it or an alternative way to generate the URL.
I tried to use RouteLink and embed it in the Form tag, but RouteLink creates me the full HTML for an <A> tag which is no good.
action="<%= Ajax.RouteLink("Update Status", "product-route-short", new { action = "GetStatus", sku = "" }, new AjaxOptions { UpdateTargetId = "status" })%>"
What alternatives do i have to generate a URL from a named route.
Should I report this missing overload as an issue?
If you need to generate a URL to a named route, you can use Url.RouteUrl(), you just have to put the controller and action into the route values object like so:
<%= Url.RouteUrl("NamedRoute", new { controller="Controller", action="Action", foo="Bar" ... }) %>
As for adding the overload, a lot of these helpers have so many different overloads that they start to conflict and become ambiguous. For example, consider the following overloads
public string BeginForm(string actionName, string controllerName)
public string BeginForm(string actionName)
public string BeginForm(string routeName, string actionName) // Uh oh!
The 1st and 3rd have identical signatures, so they are invalid.
You could always create your own form helper as an extension method to HtmlHelper if you need to use Named Routes often.

Resources