Unable to get property 'value' of undefined or null reference - asp.net
I have registration form and button. OnClick - I call function on server side which make a validation of user's zip code at Database with Zipcodes. If validation passed successfully - user's data stored in Database (here I continue use server function). But if ZipCode does not match - I call Javascript function where I ask if user still wants to save his data to DB. and If yes - I save it using Ajax request. Problem is when I call Javascript function - firstly it should receive user's data on client side. But when reading data happens - I receive an error "Unable to get property 'value' of undefined or null reference". But user's data still exist at the form's fields. It seems that the data that read by the server from the form once - reset somewhere - and can not be read a second time on the client.
Here is my ASP Form
<body>
<form id="frmZipValidation" runat="server">
<div>
<asp:Label runat="server">Registration Form</asp:Label>
<asp:TextBox runat="server" ID="txtbxName"></asp:TextBox>
<asp:TextBox runat="server" ID="txtbxZipCode"></asp:TextBox>
<asp:DropDownList runat="server" ID="DDLCountry">
<asp:ListItem Text="Select country" Value="Select" Selected="True"></asp:ListItem>
<asp:ListItem Text="USA" Value="USA"></asp:ListItem>
<asp:ListItem Text="Canada" Value="Canada"></asp:ListItem>
</asp:DropDownList>
<asp:TextBox runat="server" ID="txtbxState"></asp:TextBox>
<asp:TextBox runat="server" ID="txtbxCity"></asp:TextBox>
<asp:Button runat="server" ID="btnSubmit" Text="Submit" OnClick="btnSubmit_Click"/>
</div>
</form>
</body>
Here is my Server Side
public partial class Default : System.Web.UI.Page
{
string Name;
string ZipCode;
string Country;
string State;
string City;
bool IsMatch;
Addresses dbAddresses = new Addresses();
User newUser;
protected void Page_Load(object sender, EventArgs e)
{
if (Request["Action"] != null && Request["Action"].Trim() != "")
{
if (Request["Action"] == "AddUser")
{
AddUser(Request["Name"], Request["ZipCode"], Request["Country"], Request["State"], Request["City"]);
}
}
}
private void AddUser(string UserName, string UserZip, string UserCountry, string UserState, string UserCity)
{
newUser = new User(UserName, UserZip, UserCountry, UserState, UserCity);
dbAddresses.Users.Add(newUser);
dbAddresses.SaveChanges();
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
if (IsValid)
{
ZipCode = txtbxZipCode.Text;
Country = DDLCountry.Text;
State = txtbxState.Text;
City = txtbxCity.Text;
Name = txtbxName.Text;
IsMatch = false;
List<ZipCode> ZipC = (from z in dbAddresses.Zips
where z.Zip == ZipCode
select z).ToList();
//If ZipCode entered by client do not exists at Database return false
if (!ZipC.Any())
{
IsMatch = false;
}
else
{
for (int i = 0; i < ZipC.Count; i++)
{
if (ZipC[i].Country.ToString() == Country)
{
if (ZipC[i].State.ToString() == State)
{
if (ZipC[i].City.ToString() == City)
{
AddUser(Name, ZipCode, Country, State, City);
//Message to the user that all saved successfully
Page.ClientScript.RegisterClientScriptBlock(typeof(Page), "1", "<script>alert('Your data was saved successfully!');</script>");
IsMatch = true;
break;
}
else
{
IsMatch = false;
break;
}
}
else
{
IsMatch = false;
break;
}
}
else
{
IsMatch = false;
break;
}
}
}
//If user's data are not match, then go to JS client code where - If user wants in any case to save data - make it using AJAX request
if (!IsMatch)
{
string clientScript = "AjaxRequestSaveToDB();";
this.Page.ClientScript.RegisterStartupScript(this.GetType(), "MyClientScript", clientScript);
}
}
}
}
And here is Javascript:
function AjaxRequestSaveToDB()
{
var SaveData = confirm('Zip/Postal code doesn’t match region. Are you sure you want to save this data?');
if (SaveData)
{
var UserName = document.getElementById('txtbxName').value;
var UserZipCode = document.getElementById('txtbxZipCode').value;
var UserCountry = document.getElementById('DDLCountry').value;
var USerState = document.getElementById('txtbxState').value;
var UserCity = document.getElementById('txtbxCity').value;
SendDataToServer('AddUser', UserName, UserZipCode, UserCountry, USerState, UserCity);
alert("You data was saved successfully!");
}
else { alert('Not saved');
}
}
}
function SendDataToServer(RequestType, Name, ZipCode, Country, State, City)
{
var xmlHttp = getXmlHttp();
var Url = "Default.aspx?Action=" + escape(RequestType)
+ "&Name=" + escape(Name)
+ "&ZipCode=" + escape(ZipCode)
+ "&Country=" + escape(Country)
+ "&State=" + escape(State)
+ "&City=" + escape(City);
xmlHttp.open("GET", Url, true);
xmlHttp.send();
}
A short book about Client-Server Communications using "Custom" AJAX requests.
In ASP.net programming (almost) every time the client interacts with the server, the client sends all of its information to the server and then throws out its old content and replaces it with the response the client received from the server. So the problem you were running into is that your asp:button on the client machine was sending information to your .aspx page on the server and the server was interpreting the information, realizing something was wrong and telling the client it should ask the user for more information but throw out all the information that had been previously entered.
The best way that I have found to get around this problem is to use what I call "custom AJAX requests." Basically this means that we write a string of XML and send it to an ASP handler page which is set up to accept the XML string and do something with it. In my travels I have slimmed this down to basically 3 parts. The first is the user interface which contains all of the markup and CSS(and validation), the second is the JavaScript file that contains all of the data gathering and the actual AJAX request and lastly there is the ashx file that handles the request from the client.
So to start you will need to set up your user interface. Something along the lines of:
<body>
<form id="frmZipValidation" runat="server">
<div>
<div class="label">Registration Form<div>
<asp:TextBox ID="txtbxName" class="txtbxName" ClientIDMode="Static" runat="server"></asp:TextBox>
<asp:TextBox ID="txtbxZipCode" class="txtbxZipCode" ClientIDMode="Static" runat="server" ></asp:TextBox>
<asp:DropDownList ID="DDLCountry" class="DDLCountry" ClientIDMode="Static" runat="server" >
<asp:ListItem Text="Select country" Value="Select" Selected="True"></asp:ListItem>
<asp:ListItem Text="USA" Value="USA"></asp:ListItem>
<asp:ListItem Text="Canada" Value="Canada"></asp:ListItem>
</asp:DropDownList>
<asp:TextBox ID="txtbxState" class="txtbxState" ClientIDMode="Static" runat="server" ></asp:TextBox>
<asp:TextBox ID="txtbxCity" class="txtbxCity" ClientIDMode="Static" runat="server" ></asp:TextBox>
<input id="btnSubmit" class="btnSubmit" type="button" value="Save" onclick="SubmitForm()" />
</div>
</form>
</body>
Couple things to note with this:
The button to submit the form is NOT an ASP button but a HTML button.
All of the input controls are ASP controls but they have the ClientIDMode set to Static, this will only work in .NET 4.0 or higher.
We set the class to the same thing as the ID in case we aren't using .NET 4.0 or higher. Any CSS classes that you want to also add to the control can be added after the dummy ID class.(for my examples I'm assuming you are in .NET 4.0 but I can easily switch them to work without the ClientIDMode attribute if you need)
The second piece to the puzzle is the JavaScript. There are a couple ways that we can accomplish what we need. The first is by using vanilla JS without the help of any plugins or external libraries. This saves a very small amount of processing time, a marginal amount of loading time and can accomplish everything we ask of it. But, if we include an external library, JQuery, and plugin, JQuery Validation, then we can make our lives a whole heck of a lot easier during the programming phase by reducing the amount of code we have to write by a factor of about 10. And if we are really concerned about the load times then we can use the client cache to store the external libraries so that they only have to download them once. So whether or not you decide to use any external JavaScript libraries is up to what your project needs but since you are only concerned with validating that the zip code is not empty I will not use any JQuery but I just thought it would be worth mentioning because of how streamlined it makes the process.
Once you are ready to submit your form your first step will be to validate that the zipcode is valid. You can do this a couple ways depending on how in depth you want to get. The quickest check would just be to verify that the zip code text box is not empty when the button is clicked. So to do that we would just need to do:
function SubmitForm() { //This will be assigned as the click handler on your button in your HTML
if (document.getElementById('txtbxZipCode').value != null && document.getElementById('txtbxZipCode').value != '') {
Save('YourHandler', GetQueryString, GetXmlString, SuccessHandler, FailureHandler);
} else {
//Your user needs to know what went wrong...
}
}
So, down to the meat and potatoes of this whole situation. The AJAX request. I've come up with a reusable function that handles the entire AJAX request that looks like:
function Save(handlerName, GetQueryString, GetXmlString, SuccessHandler, FailureHandler) {
// Date.GetTime gets the number of milliseconds since 1 January 1970, so we divide by 1000 to get the seconds.
end = (new Date().getTime() / 1000) + 30;
//This variable is the actual AJAX request. This object works for IE8+ but if you want backwards compatability for earlier versions you will need a different object which I can dig up for you if you need.
var xmlhttp = new XMLHttpRequest();
//This is the function that fires everytime the status of the request changes.
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
//Get all the headers to determine whether or not the request was successful. This is a header you will need to add to the response manually.
var xx = xmlhttp.getResponseHeader("Success");
//the object xx will be a string that you designate. I chose to use True as the indicator that it was successful because it was intuitive.
var x1 = xx.trim();
if (x1 != undefined && x1 == 'True' && (new Date().getTime() / 1000) < end) {
//If the response was successful and the timeout hasn't elapsed then we get the XML from the response and call the success handler
var xmlResponse = xmlhttp.responseXML;
SuccessHandler(sender, xmlResponse);
} else if ((new Date().getTime() / 1000) < end) {
//If the response was not successful and the timeout hasn't elapsed then we get the XML from the response and call the failure handler
var xmlResponse = xmlhttp.responseXML;
FailureHandler(sender, xmlResponse);
} //If the request was successful
} //If the readystate is 4 and the status is 200
} //OnReadyStateChanged function
//This gets the query string to be added to the url
var varString = GetQueryString();
//Build XML string to send to the server
var xmlString = GetXmlString();
//Open the request using the handler name passed in and the querystring we got from the function passed in
xmlhttp.open("POST", "../RequestHandlers/" + handlerName + ".ashx" + varString, true);
//This tells the handler that the content of the request is XML
xmlhttp.setRequestHeader("Content-Type", "text/xml");
//Send the request using the XML we got from the other function passed in.
xmlhttp.send(xmlString);
}
This function has a built in timeout which makes it so that if the server takes more than 30 seconds to respond to a request then any response that the client receives is ignored. For my implementations this is combined with another function that displays something to the user to tell them that the website is working on their request and if the time out elapses it tells them that a time out occurred.
The second thing this function does is it assumes that all handlers will be in a folder next to the root of your website named RequestHandlers. I use this set up just to consolidate all of my handler files but you can really change where it is looking to wherever you want.
The function itself takes in a string and four function pointers. The string represents the name of the handler that will be waiting to interpret the request, the four function pointers all have very specific jobs.
The first function pointer is GetQueryString this represents a function you will have to write that will append any variables that you deem necessary to the end of the URL being posted back to. This site gives a pretty accurate explanation of what the query string should be used for. For me a common GetQueryString function looks something like:
function GetPaymentQueryString() {
var varString = '';
varString = "?CCPayment=True";
return varString;
}
The second function pointer, GetXMLString, is used to create the XML string(go figure...) that will be sent to the handler page that we are posting back to. This string will represent the bulk of the request. Everything that should not be shown to anyone snooping your requests should be sent as an XML string, if you are really paranoid you can send it as an encrypted XML string but that's not, strictly speaking, necessary. It all depends on what you are sending, if its complete credit card information then, yeah, maybe you would want to consider it, but if its first and last names then encrypting it would be overkill.
A common GetXMLString function might look like:
function GetPaymentXmlString() {
var xmlString = '';
xmlString = '<?xml version="1.0" encoding="UTF-8"?><Address><ZipCode>' + document.getElementById('txtbxZipCode').value + '</ZipCode></Address>';
return xmlString;
}
The important part of that function is to get your XML right. The first tag is pretty universal and should be fine to use in most situations and then after that its all just matching the tags up. I left out a lot of your fields to save space.
The last two function pointers are what you will want to call if everything goes as planned and if something fails respectively. The way that I normally handle successful requests is to hide the inputs as a whole(usually by putting them inside of their own div section) and displaying a confirmation message of some sort. Failed requests can be a bit trickier because you have to tell the user why they failed. The way that I do that is by having a dummy div section above everything else on the page with some sort of special CSS attached to it that makes the div stand out in some way and if the request fails then I send a string of text from the server with my best guess of why it failed and assign it to the be displayed in the div section. How you decide to display the results to the user is obviously all dictated by the project itself. Since what you do when it succeeds or fails is basically on a project by project basis I can't really give a good generic example of what you should do so for this part you are on your own.
Now that we have those pieces in place, the last piece to make is the handler.
Basically for all intents and purposes a handler is basically an ASPX webpage with nothing on it. So the HTML that makes up your handler pages, which have the extension .ashx, will look like:
<%# WebHandler Language="VB" CodeBehind="YourHandler.ashx.cs" Class="YourHandler" %>
And that's it. There should be no other markup in your actual .ashx file. Obviously the name of the handler will change depending on what you are doing.
The code behind when creating an ashx file by default will be a class that contains a single function named ProcessRequest. Basically you can treat this function as a sort of "request received" event. So in your case you would move the content of your btnSubmit_Click function to the ProcessRequest function in the ashx file. You can add any properties or other functions that you want but the ProcessRequest function must be present for the handler to work as far as I know.
One extra step that you will need to do is to get the information from the XML that was sent to your handler and also tell the response that you will be sending XML back to the client.
So to get the XML from the request you will need to do:
IO.StreamReader textReader = New IO.StreamReader(context.Request.InputStream);
context.Request.InputStream.Seek(0, IO.SeekOrigin.Begin);
textReader.DiscardBufferedData();
XDocument xml = XDocument.Load(textReader);
String zip = xml.Elements("Address").Elements("ZipCode").FirstOrDefault().Value;
In order to send XML back to the client you will need to add a couple headers to the response and you accomplish that by adding(I think this is the correct way to implement an interface in C# not positive on this point though):
class YourHandler : System.Web.IHttpHandler, System.Web.SessionState.IReadOnlySessionState
under your class definition and:
context.Response.ContentType = "text/xml";
context.Response.ContentEncoding = System.Text.Encoding.UTF8;
context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
context.Response.Cache.SetAllowResponseInBrowserHistory(True);
to the beginning of your ProcessRequest function. Those six lines tell the client it will be receiving XML and not to cache any of the response which will ensure that your clients always see the most up-to-date content.
So. There it is. You should now have the framework to validate user input, create an AJAX request, send the request to a custom handler, accept XML from the client, write XML to the client and display the res-...I knew I forgot something...
What is the client supposed to do with the XML it gets from the server? throw it at the wall and see what sticks? No that won't work. You'll need a way to interpret the XML on the client side. Luckily the XMLHttpRequest object has been written to make this task a lot easier than it sounds.
You may have noticed that I set up my success and failure handlers to take a sender object and an XML object. The sender is really overkill and can be ignored(or removed) for this example to work fine. The XML object is what we are concerned with for now. Before we even get into the client side I must mention that you will have to go through the same process on the server side as you did on the client side and manually write your XML string including all the values you want the client to know about. For this example I'm going to assume you want to display a FriendlyMessage to the user. To write the response to the client you will do something like:
using (System.Xml.XmlTextWriter writer = new System.Xml.XmlTextWriter(context.Response.Output)) {
context.Response.AddHeader("Success", true);
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.LoadXml("<?xml version='1.0' encoding='UTF-8'?><Response><FriendlyMessage>" + Message + "</FriendlyMessage></Response>");
doc.WriteTo(writer);
writer.Flush();
writer.Close();
}
On the client side to get the FriendlyMessage from the XML you will need to do:
xml.getElementsByTagName("FriendlyMessage")[0].childNodes[0].nodeValue
Now this line makes a few assumptions. Like, you may want to add some checks in to make sure xml.getElementsByTagName("FriendlyMessage") actually has children before trying to evaluate them. Those sorts of checks are up to your discretion.
This time I think I've actually covered all the steps. I hope my "little" guide helps you and I didn't bore you too much. I apologize for the length but its sort of a process so getting it right takes a few steps. Once you get the base line in place and working it really lends itself to any situation. This layout also makes your user experience much better than having them wait for full trips to the server each time.
I sincerely hope this helps you get your project done and that I haven't skipped a step or something equally as embarrassing...
Related
AjaxFileUpload: How can I alert the user to a server-side error in OnUploadComplete?
I've got the AjaxFileUpload control working just fine -- it uploads, and on completion calls the server-side code to move the files around, works just fine, etc etc etc. What I'm worried about are potential server-side errors, and how to hand back some sort of warning to the user. Not an Ajax error, but something on the server-side code. Now, I've got log4net running just fine, and it's called in my error-trapping code and merrily dumping logs when I hard-code an error. But that doesn't tell the users anything. RegisterStartupScript doesn't seem to be a valid solution, because there's no PostBack that would allow it to operate (same as my first attempt at populating fields. doh!). Now, I can shove some JS into the ClientUploadComplete or ClientUploadCompleteAll to do a PostBack... but that doesn't seem right, and it would require that server-side error-messages be queued-up for display. Plus, it clears out the AjaxFileUpload display of what has been uploaded. All the error-handling I've seen regarding these controls is for Ajax errors. Is there anything for server-side issues that allows for easy feedback to the user? Am I even barking up the right trees? UPDATE Using the pointers # Yuriy's other answer I've got the following working: Onn the server side: protected void OnUploadComplete(object sender, AjaxFileUploadEventArgs e) { try { // does something with the uploaded files that _might_ fail } catch (Exception ex) { var ce = Logger.LogError(ex); var msg = string.Format("{{ 'id': '{0}', 'message': '{1}'}}", ce.ErrorId, ce.Message); e.PostedUrl = msg; } } (The Logger dumps the complete exception into a log4net log, and returns an error-number and consumer-friendly message to contact support) Back on the page, the AjaxFileUpload control is configured to call the JS when complete: <asp:AjaxFileUpload runat="server" ID="Uploader" MaximumNumberOfFiles="10" OnUploadComplete="OnUploadComplete" OnClientUploadComplete="AjaxFileUpload1_OnClientUploadComplete"/> And the javascript: <script type="text/javascript"> function AjaxFileUpload1_OnClientUploadComplete(sender, args) { var errText = args.get_postedUrl(); if (!errText) return; // only process if populated var errinfo = Sys.Serialization.JavaScriptSerializer.deserialize(errText); if (errinfo && errinfo.id && errinfo.message) { var idEl = document.getElementById('errnbr'); var msgEl = document.getElementById('errmsg'); if (idEl && msgEl) { idEl.innerHTML = errinfo.id; msgEl.innerHTML = errinfo.message; } } } </script> which populates the following: <div class="failureNotification" id="ErrorDisplay" runat="server"> <span id="errnbr"><asp:Literal ID="ErrorNumber" runat="server"></asp:Literal></span> <span id="errmsg"><asp:Literal ID="FailureText" runat="server"></asp:Literal></span> </div>
Although AjaxFileUploadState enumeration include Failed member I can't find any case where it used. So there are two solutions available I believe. The first is to tweak ACT project to add setters to State and StatusMessage properties of AjaxFileUploadEventArgs class and handle values of these properties on client in raiseUploadComplete function of Sys.Extended.UI.AjaxFileUpload.Control class and onUploadCompleteHandler of Sys.Extended.UI.AjaxFileUpload.ProcessorHtml5 class. Or you can pass custom JSON to client via AjaxFileUploadEventArgs.PostedUrl property, deserialize it on client in OnClientUploadComplete handler and show error message if any. Please check this question for sample of usage PostedUrl property: getting asynfileupload controls file name on button click
How to display save confirmation message on my Web form when user saves new form by clicking Save button
I am very new to ASP.NET. I am working on some other developer's Web form. I have been asked to fix a bug which says: No success message is displayed when user clicks Save button after adding information to the form (first time or while updating) There is a Label at the top of the page with ID lblMsgs <asp:Label ID="lblMsgs" runat="server"> I have added this to the Code behind in the appropriate place: lblMsgs.Text = "Message Text"; Now, my question is how do I modify the HTML or code behind to make sure that when Save button is clicked, the code checks that the data is saved to the database and displays save successful message on the Web form. Can you please help by giving example of the code using my label ID above? Thank you in advance.
That's a pretty vague question, but if your looking for data validation you should look at the built in ASP.NET Validators. This can do client/server side data validation. As for returning a message to the user based on a successful update to the DB, that is going to be solution specific depending on what type of data layer your using.
On your Save button handler, you can add validation logic if whether the data is valid or not. protected void ValidateSave(object o, EventArgs e){ if (Page.IsValid) { // Save to DB and notify of update status var success = SomeDBMethod.Update... if (success){ DoAlert("Saved Successfully!"); } } else { lblMsgs.Text = "Failed"; } } // Call client-side alert private void DoAlert(string message) { ClientScriptManager cs = Page.ClientScript; var x = "alertScript"; if (!cs.IsStartupScriptRegistered(cstype, x)) { String script = string.Format"alert('{0}', message);"; cs.RegisterStartupScript(cstype, x, script, true); } }
jQuery UI autocomplete is not displaying results fetched via AJAX
I am trying to use the jQuery UI autocomplete feature in my web application. What I have set up is a page called SearchPreload.aspx. This page checks for a value (term) to come in along with another parameter. The page validates the values that are incoming, and then it pulls some data from the database and prints out a javascript array (ex: ["item1","item2"]) on the page. Code: protected void Page_Load(object sender, EventArgs e) { string curVal; string type =""; if (Request.QueryString["term"] != null) { curVal = Request.QueryString["term"].ToString(); curVal = curVal.ToLower(); if (Request.QueryString["Type"] != null) type = Request.QueryString["Type"].ToString(); SwitchType(type,curVal); } } public string PreLoadStrings(List<string> PreLoadValues, string curVal) { StringBuilder sb = new StringBuilder(); if (PreLoadValues.Any()) { sb.Append("[\""); foreach (string str in PreLoadValues) { if (!string.IsNullOrEmpty(str)) { if (str.ToLower().Contains(curVal)) sb.Append(str).Append("\",\""); } } sb.Append("\"];"); Response.Write(sb.ToString()); return sb.ToString(); } } The db part is working fine and printing out the correct data on the screen of the page if I navigate to it via browser. The jQuery ui autocomplete is written as follows: $(".searchBox").autocomplete({ source: "SearchPreload.aspx?Type=rbChoice", minLength: 1 }); Now if my understanding is correct, every time I type in the search box, it should act as a keypress and fire my source to limit the data correct? When I through a debug statement in SearchPreload.aspx code behind, it appears that the page is not being hit at all. If I wrap the autocomplete function in a .keypress function, then I get into the search preload page but still I do not get any results. I just want to show the results under the search box just like the default functionality example on the jQuery website. What am I doing wrong?
autocomplete will NOT display suggestions if the JSON returned by the server is invalid. So copy the following URL (or the returned JSON data) and paste it on JSONLint. See if your JSON is valid. http://yourwebsite.com/path/to/Searchpreload.aspx?Type=rbChoice&term=Something PS: I do not see that you're calling the PreLoadStrings function. I hope this is normal.
A couple of things to check. Make sure that the path to the page is correct. If you are at http://mysite.com/subfolder/PageWithAutoComplete.aspx, and your searchpreload.aspx page is in another directory such as http://mysite.com/anotherFolder/searchpreload.aspx the url that you are using as the source would be incorrect, it would need to be source: "/anotherFolder/Searchpreload.aspx?Type=rbChoice" One other thing that you could try is to make the method that you are calling a page method on the searchpreload.aspx page. Typically when working with javascript, I try to use page methods to handle ajax reqeusts and send back it's data. More on page methods can be found here: http://www.singingeels.com/Articles/Using_Page_Methods_in_ASPNET_AJAX.aspx HTH.
Passing flash variables to asp.net
I don't know much about Flash but we are working on a site that has a flash form and when the users pick an option, like selecting a value from a drop down list, we need the value to be passed to asp.net server-side code. What's the easiest way to do this?
Flash can invoke server side service. So use GET or POST to pass data
You could explore these options: 1) Communicate between the SWF and the containing page through JavaScript 2) Communicate via asp.net webservices from the SWF directly to the webservice. 3) Not sure but could probably do a POST to a processing aspx page? HTH
I think a good option is to use the XML class so consider this: var xmlRequest = new XML(); xmlRequest.onLoad = parseXMLResponse; xmlRequest.load("http://yourpathtoyourserver/file.aspx?listselectedvalue=something"); function parseXMLRequest(loaded) { trace("hi"); } You can also have the page give you data back this way so it's not just one way communication.
Assuming you are using Action Script 2. Read the important notes at the bottom of each codes pertain to sending and retrieving data from flash to .net page. Explanation of the code is in the comment inside the code. Flash Part (Action Script 2) //function to send collected form data to asp.net page //use other control/button to call this function //important: in order for the 'onLoad' event to work correctly, this function has to be 'Void' function sendForm():Void { //create LoadVars object var lv_in:LoadVars = new LoadVars(); var lv_out:LoadVars = new LoadVars(); //set onLoad event lv_in.onLoad = function(success:Boolean) { //if success, meaning data has received from .net page, run this code if (success) { //lv_in.status is use to get the posted data from .Net page statusMsg.text = "Thank you for filling up the form!" + lv_in.status; } //if fail, run this code else { statusMsg.text = "The form you are trying to fill up has an error!"; } } //this is the collected data from the form lv_out.userName = txtUserName.text; lv_out.userAddress = txtUserAddress.text; lv_out.userBirthday = txtUserBirthday.text; //begin invoke .net page lv_out.sendAndLoad("ProcessDataForm.aspx", lv_in, "POST"); } Important note: The function that contain onLoad event, in this case sendForm function, has to be Void function, meaning it's not returning value. If this function return value, what happen is the function will be executed all the way without waiting for the returned data from .net page, thus the onLoad event will not be set properly. .Net Part public void ProcessData { //process the data here Response.Write("status=processed&"); } Important note: To send data/message back to flash, you can use Response.Write. However, if you want Action Script to parse the posted message/data from .Net page keep in mind you have to include & symbol at the end of the message. When parsing data/message, Action Script will stop at & symbol, thus leave the rest of the message alone and only get the message under sent variable.
Response.Redirect with POST instead of Get?
We have the requirement to take a form submission and save some data, then redirect the user to a page offsite, but in redirecting, we need to "submit" a form with POST, not GET. I was hoping there was an easy way to accomplish this, but I'm starting to think there isn't. I think I must now create a simple other page, with just the form that I want, redirect to it, populate the form variables, then do a body.onload call to a script that merely calls document.forms[0].submit(); Can anyone tell me if there is an alternative? We might need to tweak this later in the project, and it might get sort of complicated, so if there was an easy we could do this all non-other page dependent that would be fantastic. Anyway, thanks for any and all responses.
Doing this requires understanding how HTTP redirects work. When you use Response.Redirect(), you send a response (to the browser that made the request) with HTTP Status Code 302, which tells the browser where to go next. By definition, the browser will make that via a GET request, even if the original request was a POST. Another option is to use HTTP Status Code 307, which specifies that the browser should make the redirect request in the same way as the original request, but to prompt the user with a security warning. To do that, you would write something like this: public void PageLoad(object sender, EventArgs e) { // Process the post on your side Response.Status = "307 Temporary Redirect"; Response.AddHeader("Location", "http://example.com/page/to/post.to"); } Unfortunately, this won't always work. Different browsers implement this differently, since it is not a common status code. Alas, unlike the Opera and FireFox developers, the IE developers have never read the spec, and even the latest, most secure IE7 will redirect the POST request from domain A to domain B without any warnings or confirmation dialogs! Safari also acts in an interesting manner, while it does not raise a confirmation dialog and performs the redirect, it throws away the POST data, effectively changing 307 redirect into the more common 302. So, as far as I know, the only way to implement something like this would be to use Javascript. There are two options I can think of off the top of my head: Create the form and have its action attribute point to the third-party server. Then, add a click event to the submit button that first executes an AJAX request to your server with the data, and then allows the form to be submitted to the third-party server. Create the form to post to your server. When the form is submitted, show the user a page that has a form in it with all of the data you want to pass on, all in hidden inputs. Just show a message like "Redirecting...". Then, add a javascript event to the page that submits the form to the third-party server. Of the two, I would choose the second, for two reasons. First, it is more reliable than the first because Javascript is not required for it to work; for those who don't have it enabled, you can always make the submit button for the hidden form visible, and instruct them to press it if it takes more than 5 seconds. Second, you can decide what data gets transmitted to the third-party server; if you use just process the form as it goes by, you will be passing along all of the post data, which is not always what you want. Same for the 307 solution, assuming it worked for all of your users.
You can use this aproach: Response.Clear(); StringBuilder sb = new StringBuilder(); sb.Append("<html>"); sb.AppendFormat(#"<body onload='document.forms[""form""].submit()'>"); sb.AppendFormat("<form name='form' action='{0}' method='post'>",postbackUrl); sb.AppendFormat("<input type='hidden' name='id' value='{0}'>", id); // Other params go here sb.Append("</form>"); sb.Append("</body>"); sb.Append("</html>"); Response.Write(sb.ToString()); Response.End(); As result right after client will get all html from server the event onload take place that triggers form submit and post all data to defined postbackUrl.
HttpWebRequest is used for this. On postback, create a HttpWebRequest to your third party and post the form data, then once that is done, you can Response.Redirect wherever you want. You get the added advantage that you don't have to name all of your server controls to make the 3rd parties form, you can do this translation when building the POST string. string url = "3rd Party Url"; StringBuilder postData = new StringBuilder(); postData.Append("first_name=" + HttpUtility.UrlEncode(txtFirstName.Text) + "&"); postData.Append("last_name=" + HttpUtility.UrlEncode(txtLastName.Text)); //ETC for all Form Elements // Now to Send Data. StreamWriter writer = null; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; request.ContentLength = postData.ToString().Length; try { writer = new StreamWriter(request.GetRequestStream()); writer.Write(postData.ToString()); } finally { if (writer != null) writer.Close(); } Response.Redirect("NewPage"); However, if you need the user to see the response page from this form, your only option is to utilize Server.Transfer, and that may or may not work.
Something new in ASP.Net 3.5 is this "PostBackUrl" property of ASP buttons. You can set it to the address of the page you want to post directly to, and when that button is clicked, instead of posting back to the same page like normal, it instead posts to the page you've indicated. Handy. Be sure UseSubmitBehavior is also set to TRUE.
This should make life much easier. You can simply use Response.RedirectWithData(...) method in your web application easily. Imports System.Web Imports System.Runtime.CompilerServices Module WebExtensions <Extension()> _ Public Sub RedirectWithData(ByRef aThis As HttpResponse, ByVal aDestination As String, _ ByVal aData As NameValueCollection) aThis.Clear() Dim sb As StringBuilder = New StringBuilder() sb.Append("<html>") sb.AppendFormat("<body onload='document.forms[""form""].submit()'>") sb.AppendFormat("<form name='form' action='{0}' method='post'>", aDestination) For Each key As String In aData sb.AppendFormat("<input type='hidden' name='{0}' value='{1}' />", key, aData(key)) Next sb.Append("</form>") sb.Append("</body>") sb.Append("</html>") aThis.Write(sb.ToString()) aThis.End() End Sub End Module
Thought it might interesting to share that heroku does this with it's SSO to Add-on providers An example of how it works can be seen in the source to the "kensa" tool: https://github.com/heroku/kensa/blob/d4a56d50dcbebc2d26a4950081acda988937ee10/lib/heroku/kensa/post_proxy.rb And can be seen in practice if you turn of javascript. Example page source: <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Heroku Add-ons SSO</title> </head> <body> <form method="POST" action="https://XXXXXXXX/sso/login"> <input type="hidden" name="email" value="XXXXXXXX" /> <input type="hidden" name="app" value="XXXXXXXXXX" /> <input type="hidden" name="id" value="XXXXXXXX" /> <input type="hidden" name="timestamp" value="1382728968" /> <input type="hidden" name="token" value="XXXXXXX" /> <input type="hidden" name="nav-data" value="XXXXXXXXX" /> </form> <script type="text/javascript"> document.forms[0].submit(); </script> </body> </html>
PostbackUrl can be set on your asp button to post to a different page. if you need to do it in codebehind, try Server.Transfer.
#Matt, You can still use the HttpWebRequest, then direct the response you receive to the actual outputstream response, this would serve the response back to the user. The only issue is that any relative urls would be broken. Still, that may work.
I suggest building an HttpWebRequest to programmatically execute your POST and then redirect after reading the Response if applicable.
Here's what I'd do : Put the data in a standard form (with no runat="server" attribute) and set the action of the form to post to the target off-site page. Before submitting I would submit the data to my server using an XmlHttpRequest and analyze the response. If the response means you should go ahead with the offsite POSTing then I (the JavaScript) would proceed with the post otherwise I would redirect to a page on my site
In PHP, you can send POST data with cURL. Is there something comparable for .NET? Yes, HttpWebRequest, see my post below.
The GET (and HEAD) method should never be used to do anything that has side-effects. A side-effect might be updating the state of a web application, or it might be charging your credit card. If an action has side-effects another method (POST) should be used instead. So, a user (or their browser) shouldn't be held accountable for something done by a GET. If some harmful or expensive side-effect occurred as the result of a GET, that would be the fault of the web application, not the user. According to the spec, a user agent must not automatically follow a redirect unless it is a response to a GET or HEAD request. Of course, a lot of GET requests do have some side-effects, even if it's just appending to a log file. The important thing is that the application, not the user, should be held responsible for those effects. The relevant sections of the HTTP spec are 9.1.1 and 9.1.2, and 10.3.
Typically, all you'll ever need is to carry some state between these two requests. There's actually a really funky way to do this which doesn't rely on JavaScript (think <noscript/>). Set-Cookie: name=value; Max-Age=120; Path=/redirect.html With that cookie there, you can in the following request to /redirect.html retrieve the name=value info, you can store any kind of information in this name/value pair string, up to say 4K of data (typical cookie limit). Of course you should avoid this and store status codes and flag bits instead. Upon receiving this request you in return respond with a delete request for that status code. Set-Cookie: name=value; Max-Age=0; Path=/redirect.html My HTTP is a bit rusty I've been going trough RFC2109 and RFC2965 to figure how reliable this really is, preferably I would want the cookie to round trip exactly once but that doesn't seem to be possible, also, third-party cookies might be a problem for you if you are relocating to another domain. This is still possible but not as painless as when you're doing stuff within your own domain. The problem here is concurrency, if a power user is using multiple tabs and manages to interleave a couple of requests belonging to the same session (this is very unlikely, but not impossible) this may lead to inconsistencies in your application. It's the <noscript/> way of doing HTTP round trips without meaningless URLs and JavaScript I provide this code as a prof of concept: If this code is run in a context that you are not familiar with I think you can work out what part is what. The idea is that you call Relocate with some state when you redirect, and the URL which you relocated calls GetState to get the data (if any). const string StateCookieName = "state"; static int StateCookieID; protected void Relocate(string url, object state) { var key = "__" + StateCookieName + Interlocked .Add(ref StateCookieID, 1).ToInvariantString(); var absoluteExpiration = DateTime.Now .Add(new TimeSpan(120 * TimeSpan.TicksPerSecond)); Context.Cache.Insert(key, state, null, absoluteExpiration, Cache.NoSlidingExpiration); var path = Context.Response.ApplyAppPathModifier(url); Context.Response.Cookies .Add(new HttpCookie(StateCookieName, key) { Path = path, Expires = absoluteExpiration }); Context.Response.Redirect(path, false); } protected TData GetState<TData>() where TData : class { var cookie = Context.Request.Cookies[StateCookieName]; if (cookie != null) { var key = cookie.Value; if (key.IsNonEmpty()) { var obj = Context.Cache.Remove(key); Context.Response.Cookies .Add(new HttpCookie(StateCookieName) { Path = cookie.Path, Expires = new DateTime(1970, 1, 1) }); return obj as TData; } } return null; }
Copy-pasteable code based on Pavlo Neyman's method RedirectPost(string url, T bodyPayload) and GetPostData() are for those who just want to dump some strongly typed data in the source page and fetch it back in the target one. The data must be serializeable by NewtonSoft Json.NET and you need to reference the library of course. Just copy-paste into your page(s) or better yet base class for your pages and use it anywhere in you application. My heart goes out to all of you who still have to use Web Forms in 2019 for whatever reason. protected void RedirectPost(string url, IEnumerable<KeyValuePair<string,string>> fields) { Response.Clear(); const string template = #"<html> <body onload='document.forms[""form""].submit()'> <form name='form' action='{0}' method='post'> {1} </form> </body> </html>"; var fieldsSection = string.Join( Environment.NewLine, fields.Select(x => $"<input type='hidden' name='{HttpUtility.UrlEncode(x.Key)}' value='{HttpUtility.UrlEncode(x.Value)}'>") ); var html = string.Format(template, HttpUtility.UrlEncode(url), fieldsSection); Response.Write(html); Response.End(); } private const string JsonDataFieldName = "_jsonData"; protected void RedirectPost<T>(string url, T bodyPayload) { var json = JsonConvert.SerializeObject(bodyPayload, Formatting.Indented); //explicit type declaration to prevent recursion IEnumerable<KeyValuePair<string, string>> postFields = new List<KeyValuePair<string, string>>() {new KeyValuePair<string, string>(JsonDataFieldName, json)}; RedirectPost(url, postFields); } protected T GetPostData<T>() where T: class { var urlEncodedFieldData = Request.Params[JsonDataFieldName]; if (string.IsNullOrEmpty(urlEncodedFieldData)) { return null;// default(T); } var fieldData = HttpUtility.UrlDecode(urlEncodedFieldData); var result = JsonConvert.DeserializeObject<T>(fieldData); return result; }