ASP.NET MVC - HTML.BeginForm and SSL - c#

I am encountering an issue with what should be a simple logon form in ASP.NET MVC 2. Essentially my form looks a little something like this:
using (Html.BeginForm("LogOn", "Account", new { area = "Buyers" }, FormMethod.Post, new { ID = "buyersLogOnForm" }))
I have a RequiresHTTPS filter on the LogOn Action method but when it executes I receive the following message
The requested resource can only be
accessed via SSL
At this point the only solution that worked was to pass in an extra action htmlattribute as follows:
var actionURL = "https://" + Request.Url.Host + Request.Url.PathAndQuery;
using (Html.BeginForm("LogOn", "Account", new { area = "Buyers" }, FormMethod.Post, new { ID = "buyersLogOnForm", #action = actionURL }))
While this works I wonder a) why i am seeing this issue in the first place and b) if there is a more straightforward way of posting to https from a http page?
[Edit]
I should have stated that the logon dropdown will be available on many public pages. I do not want all of my pages to be HTTPS. For instance, my hope page - which ANYONE can see - should not be HTTPS based. Essentially I need to specify the protocol in my form but have no idea how to do that, or if it is possible.
I would appreciate any advice/suggestions.
Thanks in advance
JP

You could use
<form action =" <%= Url.Action(
"action",
"controller",
ViewContext.RouteData.Values,
"https"
) %>" method="post" >

Use the [RequireHttps] attribute on both the action that renders the form and the one you are posting to.

Update: Review the comments below about the security vulnerabilities of this approach before considering the use of this code.
I found that a hybrid of JP and Malcolm's code examples worked.
using (Html.BeginForm("Login", "Account", FormMethod.Post, new { #action = Url.Action("Login","Account",ViewContext.RouteData.Values,"https") }))
Still felt a bit hacky though so I created a custom BeginForm helper. The custom helper is cleaner and does not require https when running locally.
public static MvcForm BeginFormHttps(this HtmlHelper htmlHelper, string actionName, string controllerName)
{
TagBuilder form = new TagBuilder("form");
UrlHelper Url = new UrlHelper(htmlHelper.ViewContext.RequestContext);
//convert to https when deployed
string protocol = htmlHelper.ViewContext.HttpContext.Request.IsLocal == true? "http" : "https";
string formAction = Url.Action(actionName,controllerName,htmlHelper.ViewContext.RouteData.Values,protocol);
form.MergeAttribute("action", formAction);
FormMethod method = FormMethod.Post;
form.MergeAttribute("method", HtmlHelper.GetFormMethodString(method), true);
htmlHelper.ViewContext.Writer.Write(form.ToString(TagRenderMode.StartTag));
MvcForm mvcForm = new MvcForm(htmlHelper.ViewContext);
return mvcForm;
}
Example usage:
#using (Html.BeginFormHttps("Login", "Account"))

Related

How to open new window from MVC Controller?

This is my code
public ActionResult Contact()
{
//var getRequest = (HttpWebRequest)WebRequest.Create("https://brisol.net/av-d");
//var getResponse = (HttpWebResponse)getRequest.GetResponse();
//return new HttpWebResponseResult(getResponse);
return ExternalGet("https://sample.net/av-d");
}
/// <summary>
/// Makes a GET request to a URL and returns the relayed result.
/// </summary>
private HttpWebResponseResult ExternalGet(string url)
{
var getRequest = (HttpWebRequest)WebRequest.Create(url);
var getResponse = (HttpWebResponse)getRequest.GetResponse();
return new HttpWebResponseResult(getResponse);
}
In the above code when i click contact link it open the below url with in the same window,but i want to open new window from mvc controller.
You can use target for blank page
HTML Code
<input type="button" target="_blank"/>
MVC Code
#Html.ActionLink(" ", "ActionName", "ControllerName", null, new { target = "_blank", #class = "btn btn-primary fa fa-list"})
That is not an issue of the MVC. It depends on your HTML code. The link which should be opened in a new window should look like this:
Click me
Please, notice the target attribute. However, keep in mind that it is up to the browser whether it opens new window or just a new tab.
Things like that should be done from the view or from javascript not from the controller.
javascript:
window.open("Link URL")
razor
#Html.ActionLink("bla", "Action", new {controller="Controller"}, new {target="_blank"})
or
bla
New tab/window can be controlled from your html page actually, not from server side code.
Use target='_blank' attribute in your anchor (<a>) tag.
If you are using plain HTML, do:
MDN
For rajor helper, do
#Html.ActionLink("visit the page", "action", "controller", new {target="_blank"})
Here is the help page:
https://developer.mozilla.org/en/docs/Web/HTML/Element/a
Dim strScript As String = "window.open('../Newpage.aspx', 'new_window');"
Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), "Open window", strScript, True)
Below Code will help you
Controller:-
public ActionResult ABC()
{
if(Request.HttpMethod == "POST")
return Redirect("http://www.google.com");
else
return View();
}
View:-
#using (Html.BeginForm("ABC", "Test", FormMethod.Post, new { target = "_blank" }))
{
<input type="submit" value="Contact" id="btnsubmit" />
}
Above example is just give you idea how to open new url from the controller you need to set this idea in your code.
In this example controller is Test , when you post the request like click on Contact then post the view with target = “_blank” so response of the controller action will be open in new window.

Transform Querystring to RouteValue in Razor View

I'm stuck with a very basic detail in a view.
I want to be able to let the user filter the results in the Index view.
To do this I've created a dropdown list, which gets populated thourgh my viewmodel:
#using (Html.BeginForm("Index", "Captains", FormMethod.Get)) {
<div class="row">
<div class="dropdown">
#Html.DropDownList("Name", new SelectList(Model.SomeProperty), new { id = "FilterList" })
</div>
</div>
#* ... *#
}
Additionally I have a small jQuery snippet to submit the form on the change event:
$('#FilterList').on('change', function () {
var form = $(this).parents('form');
form.submit();
});
The route I have created for this looks like this:
routes.MapRoute(
name: "IndexFilter",
url: "{controller}/{action}/{Name}",
defaults: new { Name = UrlParameter.Optional}
);
After the submit event I get redirected to the url /Index?Name=ChosenValue
This is filtering totally correct. However I'd like to get rid of the querystring and transform the route to /Index/ChosenValue.
Note: "Name", "ChosenValue" & "SomeProperty" are just dummy replacements for the actual property names.
Instead of submitting the form, you can concatenate /Captains/Index/ with the selected value of the dropdown and redirect to the url using window.location.href as below
$('#FilterList').on('change', function () {
window.location.href = '/Captains/Index/' + $(this).val();
});
I think you're looking for the wrong routing behavior out of a form submit. The type of route resolution that you're hoping to see really only happens on the server side, where the MVC routing knows all about the available route definitions. But the form submission process that happens in the browser only knows about form inputs and their values. It doesn't know that "Name" is a special route parameter... it just tacks on all the form values as querystring parameters.
So if you want to send the browser to /Index/ChosenValue, but you don't want to construct the URL from scratch on the client, you need to construct the URL on the server when the view is rendering. You could take this approach:
<div class="row">
<div class="dropdown">
#Html.DropDownList("Name", new SelectList(Model.SomeProperty),
new {
id = "FilterList",
data_redirect_url = #Url.Action("Index", "Captains", new { Name = "DUMMY_PLACEHOLDER" })
})
</div>
</div>
Above you're setting the URL with a dummy "Name" value that you can replace later, then you'll do the replacement with the selection and redirect in javascript:
$('#FilterList').on('change', function () {
var redirectUrl = $(this).data('redirect-url');
window.location.href = redirectUrl.replace("DUMMY_PLACEHOLDER", $(this).val());
});
If you are wanting to drop the query string off the url because it looks weird, then change your FormMethod.Post.
However, to really answer your question, I've tried the following successfully (Note: this might be considered a hack by some)
In short: update the action url on the form element when the list changes, client side.
$('#FilterList').on('change', function () {
var form = $(this).parents('form');
var originalActionUrl = form.attr("action");
var newActionUrl = originalActionUrl + "/" + $(this).val();
form.attr("action", newActionUrl);
console.log(form.attr("action"));
form.submit();
});
You will need to change your controller's signature to match whatever optional param value you specify in your route config. In your example, "Name".

Redirect to MVC ActionResult with Parameters when DropDownList option is changed

I am using MVC to create part of a website. In one of my Views I have a DropDownList. When a new drop down list option is selected, or in other words onchange, I want my page to be redirected to a specific Controller ActionResult. I am able to get to MyAction ActionResult if there are no parameters, however I can't figure out how to send in the needed parameters.
My Controller Action:
public virtual ActionResult MyAction(int param1, int param2)
{
return View();
}
My DropDownList in View:
#Html.DropDownList(
"viewDataItem", Model.MyEnumerableList as SelectList,
new { onchange = #"
var form = document.forms[0];
form.action='MyAction';
form.submit();"
} )
The above code calls MyAction, however it does not send in the parameters. Is there a way to somehow add the parameters to this code?
Another thought was to somehow use #{Response.Redirect(#Url.Action("MyAction", "myController", new { param1 = 2, param2= 3 }));} as my DropDownList action since Response.Redirect allows me to redirect to MyAction with parameters. Is there a way to somehow make onchanged = Response.Redirect?
The tried making onchange equal the response, but the nothing happens when I change my option:
#Html.DropDownList(
"name", Model.MyEnumerableList as SelectList,
new
{
onchange = {Response.Redirect(#Url.Action("MyAction", "controllerName", new { param1 = 5, param2 = 3 }));}
})
In short, how do I call an ActionResult with parameters whenever my DropDownList option is changed?
Similar questions were asked here and here, but the answers provide in those links all use JavaScript and I don't know how to use JS with cshtml. I tried some of those answers, but none of them solved my problems.
You can specify on the onchange event a javascript function and inside that function:
var url = '#Html.Raw(Url.Action("MyAction", "controllerName", new { param1=5, param2=2 }))';
and then:
window.location = url;
Finally the code should be:
#Html.DropDownList(
"viewDataItem", Model.MyEnumerableList as SelectList,
new { onchange = "SelectionChanged()"
} )
<script>
function SelectionChanged()
{
var url = '#Html.Raw(Url.Action("MyAction", "controllerName", new { param1=5, param2=2 }))';
window.location = url;
}
</script>
Is there a way to somehow add the parameters to this code?
Sure, there are many ways. One of them would be:
#Html.DropDownList(
"viewDataItem", Model.MyEnumerableList as SelectList,
new { onchange = #"
var form = document.forms[0];
form.action='MyAction?param1=5&param2=3';
form.submit(); /*Just make sure that form 'method' attribute is set to 'post'*/"
} )
But a much better way is described in the answer you mentioned.
Is there a way to somehow make onchanged = Response.Redirect?
Not the way you're trying to use it. onchanged is a javascript event, and javascript knows nothing about Response property or other MVC server-side stuff.

How to use RouteConfig to append a page URL

I am struggling to get my code work, but I think I've read enough to suggest this is the correct way to approach this.
On my intranet, I'd like the user to type in a single word to search into a textbox, and check a checkbox. When the new page loads, I'd like the URL rewritting services of ASP.NET MVC to kick in and change a value from
mysite.com/?id=blah&isChecked=true
to
mysite.com/home/index/blah/true
My code isn't working in the sense of it gives no error, but doesn't do what I am explaining. So, I've removed the check box to just focus on the textbox.
My only route is
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{MyType}",
defaults: new { controller = "Home", action = "Index", MyType = UrlParameter.Optional }
);
My Controller
public ActionResult Index()
{
ViewBag.Message = "Modify this";
return View();
}
[HttpGet]
public ActionResult Index(string MyType)
{
ViewBag.Message = "..." + MyType;
return View();
}
and my View has
#using (Html.BeginForm("Index", "Home",FormMethod.Get))
{
<input name="MyType" /><br />
<input type="submit" />
}
#Html.ActionLink("Click me", "Index", new { #MyType = "Blah" }) //renders correctly
The problem is, it shows the querystring still in the address bar
mysite.com/?MyType=MySearchValue
instead of
mysite.com/Home/Index/MySearchValue
You can't do this purely with routing because the browser will always send form values as query string parameters when they are part of a GET request. Once the request has been sent to the server, the MVC framework can't do anything about the URL that was used.
This leaves you with only one real option (assuming you don't want to send a custom request using JavaScript), which is to explicitly redirect to the desired URL (meaning you will always have two requests when this form is submitted).
The simplest way of doing this is simply in the controller (rather, in a separate controller to ensure that there is no conflict in method signatures):
public class FormController : Controller
{
public ActionResult Index(string MyType)
{
return RedirectToAction("Index", "MyProperController", new { MyType });
}
}
If you direct your form to this controller action, MVC will then use the routing engine to generate the proper URL for the real action and redirect the browser accordingly.
You could do this from the same controller action but it would involve inspecting the request URL to check whether a query string was used or not and redirecting back to the same action, which is a little odd.

How to Create a Friendly URL in a CS File

I know how to create a url by using html.actionlink in the aspx file. But if I want to create the same url in a code behind file how would I do that?
The code behind idea for views in MVC was removed coz it didn't really seem to fit the MVC paradigm. Maybe you should consider creating your own Html Helpers instead. Doing this, extending existing actions like Html.ActionLink() is easy (and heaps of fun).
This example shows how i created a helper to tweak my login/logout links. Some ppl might argue whether this is a good use for a helper but it works for me:
/// <summary>
/// For the global MasterPage's footer
/// </summary>
/// <returns></returns>
public static string FooterEditLink(this HtmlHelper helper,
System.Security.Principal.IIdentity user, string loginText, string logoutText)
{
if (user.IsAuthenticated)
return System.Web.Mvc.Html.LinkExtensions.ActionLink(helper, logoutText, "Logout", "Account",
new { returnurl = helper.ViewContext.HttpContext.Request.Url.AbsolutePath }, null);
else
return System.Web.Mvc.Html.LinkExtensions.ActionLink(helper, loginText, "Login", "Account",
new { returnurl = helper.ViewContext.HttpContext.Request.Url.AbsolutePath }, null);
}
..and this is how i use it in the view (partial view to be exact):
<% =Html.FooterEditLink(HttpContext.Current.User.Identity, "Edit", "Logout (" + HttpContext.Current.User.Identity.Name + ")")%>
Take a look at this post by Scott Mitchell
http://scottonwriting.net/sowblog/posts/14011.aspx
(Since you say 'html.actionlink' which is an instance of the UrlHelper class I am assuming you are in a context where you don't have access to an instance of the UrlHelper class)

Categories