I have a form where I'm collecting contact information (name, address, etc) for an e-commerce app. When the user clicks the Buy button, I want to parse the form, grab a couple of values, and generate a crypto fingerprint.
Then I want to take all of the form values (name, address, etc) from the posted form and redirect that to a new server with the same form values. I may need to inject a few new ones behind the scenes.
No problem capturing the info after the click. I'm just using a Buy action on my controller. The part I can't figure out is posting to the other server with the desired parameters.
[HttpPost]
public ActionResult Buy(BuyModel model)
{
var fingerprint = GenerateFingerprint(.....);
return Redirect("https://some.other.url.com/");
}
EDIT: To clarify. I don't need to just post the data, I actually need to display the response in the browser.
You can use the HttpWebRequest class to make the post to the other server and then redirect as normal.
var httpRequest = (HttpWebRequest)WebRequest.Create("http://example.com/mypage/;
httpRequest.Method = "POST";
httpRequest.ContentType = "application/x-www-form-urlencoded";
For the post data, you'll want to convert this to a byte[] and then add it to a stream.
string postData = "key=value&key2=value2";
byte[] dataArray = Encoding.UTF8.GetBytes(postData);
You'll then be able to set the ContentLength of the request correctly:
httpRequest.ContentLength = dataArray.Length;
Write this to a Stream and we're good to do the post:
using(Stream requestStream = httpRequest.GetRequestStream())
{
requestStream.Write(dataArray, 0, dataArray.Length);
var webResponse = (HttpWebResponse)httpRequest.GetResponse();
}
Assuming you don't need the user to be redirected to the posted page, this will work well. You can look in the webResponse object to find out what happened with the post if that's useful to you.
OK guys, here is what I ended up doing, and it works great.
For the purposes of this discussion, there are 2 views. Membership collects info from the user, and Buy executes the post to the 3rd-party server.
When the user clicks the OK button on the Membership view, the first block of code below gets executed - the HttpPost on the Membership action. Here the info is manipulated if necessary and then saved in TempData so that it can be passed to the Buy view. It then redirects control to the Buy view.
In the Buy action, the second block of code below gets executed. Here, I can grab the stuff collected in Membership and do whatever I need to do. Then copy all of the form data into ViewData[] values. You could use the model instead of ViewData, but I have other stuff in the model. Probably could have combined them, but this works.
Finally, in the Buy view, set all of the form field values using the ViewData values. The key, though is using the jQuery .ready handler so that the form gets submitted automatically when the page is done loading.
So this basically copies the form values from Membership view into the Buy view, adds a few new values and then posts it all to a 3rd-party URL.
BuyController.cs
[HttpPost]
public ActionResult Membership(OtherModel model)
{
// do some stuff with the model
// ...
TempData["OtherModel"] = model;
return RedirectToAction("Buy");
}
...
public ActionResult Buy()
{
// do some stuff to generate crypto key
var fingerprint = DoSomeStuff();
ViewData["x_fp_hash"] = fingerprint;
var otherstuff = (OtherModel) TempData["OtherModel"];
ViewData["x_login"] = otherstuff.login;
return View(model);
}
Buy.cshtml
#{
Layout = "~/Views/Shared/_LayoutBlank.cshtml";
ViewBag.Title = "Validating ...";
}
<script type='text/javascript'>
$(function () {
$("form#simForm").submit();
});
</script>
#model My.Namespace.MyModel
<form id="simForm" runat="server" method='post' action='https://xxxxxx.net/url/transact.dll'>
<input type='hidden' runat="server" name='x_login' id='x_login' value="#ViewData["x_login"]" />
<input type='hidden' runat="server" name='x_fp_hash' id='x_fp_hash'value="#ViewData["x_fp_hash"]"/>
etc.
</form>
The only way you can post the values to another url and redirect in the same post (that is, return the user to the resulting page of the post) is to actually make the users browser do the post. You're also doing the post to an HTTPS, so the whole point is to prevent man-in-the-middle kinds of things.
The only real way to do this is to return a new form to the user, and programmatically tell the browser to submit it using javascript with the new values.
Related
I have a View with a Form that calls a controller action Post Method to "Complete" a Package. It then needs to refresh the page its on as that contains information that will be updated, both within the view itself and also within a partial. It does use two different Controllers in different MVC Areas.
The Post works correctly and the redirect is issued, but the page is not refreshed.
I have read that instead, I should use OnSuccess within the Ajax call that calls Complete, but I thought that was for in page calls, not ones that navigate to different pages.
View Form
#using (Ajax.BeginForm("Complete", "Packages", new { Area = "Core" },
new AjaxOptions
{
HttpMethod = "POST"
}))
{
Core(Area) Packages Controller
[HttpPost]
public ActionResult Complete(int ID)
{
// Update code
// Refresh the full page
return RedirectToAction("Summary", new { Area = "Control", id = packageBuilder.CurrentPackage.ID });
}
Control (Area) Packages Controller
[HttpGet]
public ActionResult Summary(int id)
{
// Get Model
return View("Summary", model);
}
Any pointers would be warmly welcomed.
Thanks,
Chris.
The reason that your page is not refreshed after you submit the form and the redirect is not issued in the browser, is that you are submitting the request over AJAX. This is a request issued by the browser behind the scenes.
If you want to submit the form and for the page to be refreshed, I'd recommend changing your code from Ajax.BeginForm(... to Html.BeginForm(... and then it will load the page and perform the redirect as expected.
I am not quite sure how your ajax calls are structured, but if you are using the MVC Ajax helper you can just call `location.reload(); in the OnComplete method, like so:
#using (Ajax.BeginForm(new AjaxOptions{OnComplete = "javascriptfunction"}))
{
//Data and submit button
}
<script>
function javascriptfunction(){
location.reload();
}
</script>
I am trying to use a simple form with only a text field to get some information that will be used in an action method to redirect to a different action method. Here's the context:
I have a route mapped in my global.asax.cs file which prints "moo" the given amount of times. For example, if you typed "www.cows.com/Moo8", "Moo" would be printed 8 times. The number is arbitrary and will print however many "Moo"s as the number in the URL. I also have a form on the homepage set up as follows:
#using (Html.BeginForm("Moo", "Web"))
{
<text>How many times do you want to moo?</text>
<input type="text" name="mooNumber" />
<input type="submit" value="Moo!" />
}
The number submitted in the form should be sent to the action method "Moo" in the "Web" controller (WebController.cs):
[HttpPost]
public ActionResult Moo(int mooNumber)
{
Console.WriteLine(mooNumber);
return RedirectToAction("ExtendedMoo", new { mooMultiplier = mooNumber });
}
Finally, the "Moo" action method should send me back to the original "www.cows.com/Moo8" page; as you can see above I simply used an already existing action method "ExtendedMoo":
public ViewResult ExtendedMoo(int mooMultiplier)
{
ViewBag.MooMultiplier = RouteData.Values["mooMultiplier"];
return View();
}
How can I access the value submitted in my form and use it in the last call to "ExtendedMoo"?
Refer to this post or this, you might get some idea how routing works. Something is wrong with "www.cows.com/Moo8", try to find it out. Hint "{controller}/{action}/{parameter_or_id}"
Instead of RedirectToAction, use Redirect and create the Url.
This should do the trick:
return Redirect(Url.RouteUrl(new { controller = "Web", action = "ExtendedMoo", mooMultiplier = mooNumber }));
I hope i helps.
Oh wow. Turns out that form was on my Homepage, so instead of using "Moo" as the action method, I needed to override the "Homepage" action method with a [HttpPost] annotation over THAT one. Didn't realize that forms submitted to the page they were rendered from - that was a really useful piece of information in solving this problem!
Thanks all for your attempts at helping out!
If I understood right
You can you use form Collection to get the value from textbox.
Make Sure the input tag has both id and name properties mentioned otherwise it wont be available in form collection.
[HttpPost]
public ActionResult Moo(int mooNumber, **formcollection fc**)
{
**string textBoxVal= fc.getvalue("mooNumber").AttemptedValue;**
Console.WriteLine(mooNumber);
return RedirectToAction("ExtendedMoo", new { mooMultiplier = mooNumber });
}
I want to pass two values from view to controller . i.e., #Model.idText and value from textbox. here is my code:
#using HTML.BeginForm("SaveData","Profile",FormMethod.Post)
{
#Model.idText
<input type="text" name="textValue"/>
<input type="submit" name="btnSubmit"/>
}
But problem is if i use "Url.ActionLink() i can get #Model.idText . By post action i can get textbox value using FormCollection . But i need to get both of this value either post or ActionLink
using ajax you can achieve this :
don't use form & declare your attributes like this in tags:
#Model.idText
<input type="text" id="textValue"/>
<input type="submit" id="btnSubmit"/>
jquery:
$(function (e) {
// Insert
$("#btnSubmit").click(function () {
$.ajax({
url: "some url path",
type: 'POST',
data: { textField: $('#textValue').val(), idField: '#Model.idText' },
success: function (result) {
//some code if success
},
error: function () {
//some code if failed
}
});
return false;
});
});
Hope this will be helpful.
#using HTML.BeginForm("SaveData","Profile",FormMethod.Post)
{
#Html.Hidden("idText", Model.idText)
#Html.TextBox("textValue")
<input type="submit" value="Submit"/>
}
In your controller
public ActionResult SaveData(String idText, String textValue)
{
return null;
}
I'm not sure which part you are struggling with - submitting multiple values to your controller, or getting model binding to work so that values that you have submitted appear as parameters to your action. If you give more details on what you want to achieve I'll amend my answer accordingly.
You could use a hidden field in your form - e.g.
#Html.Hidden("idText", Model.idText)
Create a rule in global.asax and than compile your your with params using
#Html.ActionLink("My text", Action, Controller, new { id = Model.IdText, text =Model.TextValue})
Be sure to encode the textvalue, because it may contains invalid chars
Essentially, you want to engage the ModelBinder to do this for you. To do that, you need to write your action in your controller with parameters that match the data you want to pass to it. So, to start with, Iridio's suggestion is correct, although not the full story. Your view should look like:
#using HTML.BeginForm("SaveData","Profile",FormMethod.Post)
{
#Html.ActionLink("My text", MyOtherAction, MaybeMyOtherController, new { id = Model.IdText}) // along the lines of dommer's suggestion...
<input type="text" name="textValue"/>
<input type="submit" name="btnSubmit"/>
#Html.Hidden("idText", Model.idText)
}
Note that I have added the #Html.Hidden helper to add a hidden input field for that value into your field. That way, the model binder will be able to find this datum. Note that the Html.Hidden helper is placed WITHIN your form, so that this data will posted to the server when the submit button is clicked.
Also note that I have added dommer's suggestion for the action link and replaced your code. From your question it is hard to see if this is how you are thinking of passing the data to the controller, or if this is simply another bit of functionality in your code. You could do this either way: have a form, or just have the actionlink. What doesn't make sense is to do it both ways, unless the action link is intended to go somewhere else...??! Always good to help us help you by being explicit in your question and samples. Where I think dommer's answer is wrong is that you haven't stated that TextValue is passed to the view as part of the Model. It would seem that what you want is that TextValue is entered by the user into the view, as opposed to being passed in with the model. Unlike idText that IS passed in with the Model.
Anyway, now, you need to set up the other end, ie, give your action the necessary
[HttpPost]
public ActionResult SaveData(int idText, string textValue) // assuming idText is an int
{
// whatever you have to do, whatever you have to return...
}
#dommer doesn't seem to have read your code. However, his suggestion for using the Html.ActionLink helper to create the link in your code is a good one. You should use that, not the code you have.
Recapping:
As you are using a form, you are going to use that form to POST the user's input to the server. To get the idText value that is passed into the View with the Model, you need to use the Html.Hidden htmlhelper. This must go within the form, so that it is also POSTed to the server.
To wire the form post to your action method, you need to give your action parameters that the ModelBinder can match to the values POSTed by the form. You do this by using the datatype of each parameter and a matching name.
You could also have a complex type, eg, public class MyTextClass, that has two public properties:
public class MyTextClass
{
public int idText{get;set}
public string TextValue{get;set;}
}
And then in your controller action you could have:
public ActionResult SaveData(MyTextClass myText)
{
// do whatever
}
The model binder will now be able to match up the posted values to the public properties of myText and all will be well in Denmark.
HTH.
PS: You also need to read a decent book on MVC. It seems you are flying a bit blind.
Another nit pick would be to question the name of your action, SaveData. That sounds more like a repository method. When naming your actions, think like a user: she has simply filled in a form, she has no concept of saving data. So the action should be Create, or Edit, or InformationRequest, or something more illustrative. Save Data says NOTHING about what data is being saved. it could be credit card details, or the users name and telephone...
I am still new to MVC, so sorry if this is an obvious question:
I have a page where the user can choose one of several items. When they select one, they are taken to another form to fill in their details.
What is the best way to transfer that value to the form page?
I don't want the ID of the item in the second (form) pages URL.
so it's /choose-your-item/ to /redemption/ where the user sees what was selected, and fills the form in. The item selected is displayed, and shown in a hidden form.
I guess one option is to store in a session before the redirect, but was wondering if there was another option.
I am using MVC3
Darin Dimitrov's answer would be best if you don't need to do any additional processing before displaying the /redemption/ page. If you do need to some additional processing, you're going to have to use the TempDataDictionary to pass data between actions. Values stored in the TempDataDictionary lasts for one request which allows for data to be passed between actions, as opposed to the values stored in the ViewDataDictionary which only can be passed from an action to a view. Here's an example below:
public ActionResult ChooseYourItem()
{
return View();
}
[HttpPost]
public ActionResult ChooseYourItem(string chosenItem)
{
TempData["ChosenItem"] = chosenItem;
// Do some other stuff if you need to
return RedirectToAction("Redemption");
}
public ActionResult Redemption()
{
var chosenItem = TempData["ChosenItem"];
return View(chosenItem);
}
If you don't want the selected value in the url you could use form POST. So instead of redirecting to the new page, you could POST to it:
#using (Html.BeginForm("SomeAction", "SomeController"))
{
#Html.DropDownListFor(...)
<input type="submit" value="Go to details form" />
}
To help others, this is how I resolved my issue of needing multiple buttons posting back, and wanting to pass a different Id each time.
I have a single form on the page, that posts back to my controller:
The Form:
#using (Html.BeginForm("ChooseYourItem", "Item", FormMethod.Post))
{
And the code
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ChooseYourItem(string itemId)
{
TempData["ITEMID"] = itemId
return RedirectToAction("Create", "Redemption");
}
Then, inside the form, I create buttons whose name == "itemId", but has a different value each time.
For example
<strong>Item 1</strong>
<button value="123" name="itemid" id="btn1">Select</button>
<strong>Item 2</strong>
<button value="456" name="itemid" id="btn2">Select</button>
In my Facebook canvas application, I want to load an image from my ASP.NET MVC Controller.
So I do somethinhg like this, in my controller (simplified):
public ActionResult GetImage(int id)
{
// ....
return File(#"c:\temp\1.jpg", "image/jpeg");
}
And it's called from my aspx page like so:
<img src="/Home/GetImage?id=20" alt="Test"/>
But the image is not displayed, I get a "red x".
So I fire up Fiddler and see that the request does indeed return the image, but the data is prepadded with Facebook's frame html:
<html><head><script type="text/javascript">
top.location = "http://www.facebook.com/dialog/oauth/?state=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&client_id=xxxxxxxxxxxxxxx&redirect_uri=http://localhost:5000/facebookredirect.axd";
</script></head><body></body></html>?????JFIF??H?H????
?ICC_PROFILE???
????????mntrRGB XYZ ?????$?acsp???????????????????????????????????
Which of course is not vaild jpeg image data.
How can I avoid this pre padded data?
Never mind, I found out myself that the signed_request data was (yet again) missing.
I thought the only way to pass this was thru a HTTP header, but (like magic) I could pass it as a parameter as well causing the request to be validated.
<img src="/Home/GetImage?id=20&signed_request=<%=Request.Params["signed_request"] %>" />
public ActionResult GetImage(int id), string signed_request)
{
var auth = new CanvasAuthorizer();
if (auth.Authorize())
{
// Indeed, I'm now authorized
return File(#"c:\temp\1.jpg", "image/jpeg");
}
/// ...
}
/M