ISO-8859-1 to UTF8 in ASP.NET 2 - asp.net

We've got a page which posts data to our ASP.NET app in ISO-8859-1
<head>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<title>`Sample Search Invoker`</title>
</head>
<body>
<form name="advancedform" method="post" action="SearchResults.aspx">
<input class="field" name="SearchTextBox" type="text" />
<input class="button" name="search" type="submit" value="Search >" />
</form>
and in the code behind (SearchResults.aspx.cs)
System.Collections.Specialized.NameValueCollection postedValues = Request.Form;
String nextKey;
for (int i = 0; i < postedValues.AllKeys.Length; i++)
{
nextKey = postedValues.AllKeys[i];
if (nextKey.Substring(0, 2) != "__")
{
// Get basic search text
if (nextKey.EndsWith(XAEConstants.CONTROL_SearchTextBox))
{
// Get search text value
String sSentSearchText = postedValues[i];
System.Text.Encoding iso88591 = System.Text.Encoding.GetEncoding("iso-8859-1");
System.Text.Encoding utf8 = System.Text.Encoding.UTF8;
byte[] abInput = iso88591.GetBytes(sSentSearchText);
sSentSearchText = utf8.GetString(System.Text.Encoding.Convert(iso88591, utf8, abInput));
this.SearchText = sSentSearchText.Replace('<', ' ').Replace('>',' ');
this.PreviousSearchText.Value = this.SearchText;
}
}
}
When we pass through Merkblätter it gets pulled out of postedValues[i] as Merkbl�tter
The raw string string is Merkbl%ufffdtter
Any ideas?

You have this line of code:-
String sSentSearchText = postedValues[i];
The decoding of octets in the post has happen here.
The problem is that META http-equiv doesn't tell the server about the encoding.
You could just add RequestEncoding="ISO-8859-1" to the #Page directive and stop trying to fiddle around with the decoding yourself (since its already happened).
That doesn't help either. It seems you can only specify the Request encoding in the web.config.
Better would be to stop using ISO-8859-1 altogether and leave it with the default UTF-8 encoding. I can see no gain and only pain with using a restrictive encoding.
Edit
If it seems that changing the posting forms encoding is not a possibility then we seem to be left with no alternative than to handle the decoding ourselves. To that end include these two static methods in your receiving code-behind:-
private static NameValueCollection GetEncodedForm(System.IO.Stream stream, Encoding encoding)
{
System.IO.StreamReader reader = new System.IO.StreamReader(stream, Encoding.ASCII);
return GetEncodedForm(reader.ReadToEnd(), encoding);
}
private static NameValueCollection GetEncodedForm(string urlEncoded, Encoding encoding)
{
NameValueCollection form = new NameValueCollection();
string[] pairs = urlEncoded.Split("&".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
foreach (string pair in pairs)
{
string[] pairItems = pair.Split("=".ToCharArray(), 2, StringSplitOptions.RemoveEmptyEntries);
string name = HttpUtility.UrlDecode(pairItems[0], encoding);
string value = (pairItems.Length > 1) ? HttpUtility.UrlDecode(pairItems[1], encoding) : null;
form.Add(name, value);
}
return form;
}
Now instead of assigning:-
postedValues = Request.Form;
use:-
postValues = GetEncodedForm(Request.InputStream, Encoding.GetEncoding("ISO-8859-1"));
You can now remove the encoding marlarky from the rest of the code.

I think adding your encoding into web.config like that will probably solve your problem :
<configuration>
<system.web>
<globalization
fileEncoding="iso-8859-1"
requestEncoding="iso-8859-1"
responseEncoding="iso-8859-1"
culture="en-US"
uiCulture="en-US"
/>
</system.web>
</configuration>

We had the same problem that you have. The topic is not straight-forward at all.
The first tip is to set the Response encoding of the page that posts the data (usually the same page as the one that receives the data in .NET) to the desired form post encoding.
However, this is just a hint to the user's browser on how to interpret the characters sent from the server. The user might choose to override the encoding manually. And, if the user overrides the encoding of the page, the encoding of the data sent in the form is also changed (to whatever the user has set the encoding to).
There is a small trick, though. If you add a hidden field with the name _charset_ (notice the underscores) in your form, most browsers will fill out this form field with the name of the charset used when posting the form. This form field is also a part of the HTML5 specification.
So, you might think your're good to go, however, when in your page, ASP.NET has already urldecoded all parameters sent in to the form. So when you actually have the value in the _charset_ field, the value of the field containing Merkblätter is already decoded incorrectly by .NET.
You have two options:
In the ASP.NET page in question, perform the parsing of the request string manually
In Application_BeginRequest, in Global.asax, parse the request parameters manually, extracting the _charset_field. When you get the value, set Request.ContentEncoding to System.Text.Encoding.GetEncoding(<value of _charset_ field>). If you do this, you can read the value of the field containing Merkblätter as usual, no matter what charset the client sends the value in.
In either of the cases above, you need to manually read Request.InputStream, to fetch the form data. I would recommend setting the Response Encoding to UTF-8 to have the greatest number of options in which characters you accept, and then treating the special cases when the user has overridden the charset especially, as specified above.

Function urlDecode(input)
inp = Replace(input,"/","%2F")
set conn = Server.CreateObject("MSXML2.ServerXMLHTTP")
conn.setOption(2) = SXH_SERVER_CERT_IGNORE_ALL_SERVER_ERRORS
conn.open "GET", "http://www.neoturk.net/urldecode.asp?url=" & inp, False
conn.send ""
urlDecode = conn.ResponseText
End Function
To speed this up, just create a table on your db for decoded and encoded urls and read them on global.asa application.on_start section. Later put them on the application object.
Then put a check procedure for that application obj. in above function and IF decoded url not exists on app array, THEN request it one time from remote page (tip: urldecode.asp should be on different server see: http://support.microsoft.com/default.aspx?scid=kb;en-us;Q316451) and insert it to your db and append to application array object, ELSE return the function from the application obj.
This is the best method I have ever found.
If anybody wants further details on application object, database operations etc. contact me via admin#neoturk.net
You can see above method successfully working at: lastiktestleri.com/Home
I also used, HeliconTech's ISAPI_Rewrite Lite version
usage is simple: url = Request.ServerVariables("HTTP_X_REWRITE_URL")
this will return the exact url directed to /404.asp

That's because you are encoding the string as ISO-8859-1 and decoding it as if it was a string encoded as UTF-8. This will surely mess up the data.
The form isn't posting the data as ISO-8859-1 just because you send the page using that encoding. You haven't specified any encoding for the form data, so the browser will choose an encoding that is capable of handling the data in the form. It may choose ISO-8859-1, but it may just as well choose some other encoding.
The data is send to the server, where it's decoded and put in the Request.Form collection, according to the encoding that the browser specifies.
All you have to do is to read the string that has already been decoded from the Request.Form collection. You don't have to loop through all the items in the collection either, as you already know the name of the text box.
Just do:
string sentSearchText = Request.Form("SearchTextBox");

What I ended up doing was forcing our app to be in ISO-8859-1. Unfortunately the underlying data may contain characters which don't fit nicely into that codepage so we go through the data before displaying it and convert everything about the character code of 127 into an entity. Not ideal but works for us...

I had the same problem, solved like this:
System.Text.Encoding iso_8859_2 = System.Text.Encoding.GetEncoding("ISO-8859-2");
System.Text.Encoding utf_8 = System.Text.Encoding.UTF8;
NameValueCollection n = HttpUtility.ParseQueryString("RT=A+v%E1s%E1rl%F3+nem+enged%E9lyezte+a+tranzakci%F3t", iso_8859_2);
Response.Write(n["RT"]);
A+v%E1s%E1rl%F3+nem+enged%E9lyezte+a+tranzakci%F3t will return "A vásárló nem engedélyezte a tranzakciót" as expected.

Related

How can I catch a pound sign(#) from URL with multiple values in ASHX files

This is my ASHX File where I am catching multiple parameters from request URL using httpcontext, and it is working properly but when I am including a Hash(#) value in Text parameter through the URL. It is not taking the value of FLOW which is another parameter(next to Text parameter).
So it is working for:
http://localhost:10326/ussd.ashx?user=MSL&pass=MSL663055&tid=65506&msisdn=8801520101525&text=***3333**&flow=begin&momt=mo
And it is not working for:
http://localhost:10326/ussd.ashx?user=MSL&pass=MSL663055&tid=65506&msisdn=8801520101525&text=***3333#**&flow=begin&momt=mo
My ASHX files:
public void ProcessRequest(HttpContext context)
{
HttpRequest httpRequest = context.Request;
string user = httpRequest.QueryString["user"].ToString();
string pass = httpRequest.QueryString["pass"].ToString();
string tid = httpRequest.QueryString["tid"].ToString();
string msisdn = httpRequest.QueryString["msisdn"].ToString();
string text = httpRequest.QueryString["text"].ToString();
flow = httpRequest.QueryString["flow"].ToString();
HttpContext.Current.Session["user"] = user;
HttpContext.Current.Session["pass"] = pass;
HttpContext.Current.Session["tid"] = tid;
HttpContext.Current.Session["msisdn"] = msisdn;
HttpContext.Current.Session["text"] = text;
HttpContext.Current.Session["flow"] = flow;
You need to URI encode your parameter values before they are added to the URL. This way the server will not get confused by unsafe characters such as '#' which has its own meaning when included as part of a URL. See RFC 3986 Section 2.
See Encode URL in JavaScript as an example of how to encode the sent data using JavaScript. Whatever is sending the data in the URL will need to do the encoding. There is not much you can do once the request has reached the server. Without knowing how your URL is being created, I can't offer much more.
In short: the problem is with your client code not your ASHX file.

POST parameters in asp.net

I'm trying to get parameters received from a form, that were sent with method POST.
I don't know how it's called in asp, M$ loves to change stuff's names to mess with us. They come in HTTP body, while GET/QueryString parameters come in URL after the ? sign.
In PHP, "get patameters" are available in the $_GET array. In asp they are Request.QueryString["parameter1"].
"post patameters" are in $_POST, and I cant find it in asp. I hope I made it clear :p
To read the value from paramater1 contained inside the form data:
string paramater1 = Request.Form["paramater1"];
Note that if the form doesn't contain your variable, paramater1 will be null.
Suppose your querystring is something like this :
http://stackoverflow.com/questions.aspx?id=17844065&title=post-parameters-in-asp-net
if i am right then you are looking for this. Please note this is regarding ASP.Net, I have no idea about classic ASP. And this will not work on classic ASP, I believe.
You can use in cs,
if(Request["id"]!=null )
{
var id= Request["id"]; // gives you id as 17844065 string values
}
if(Request["title"]!=null )
{
var title= Request["title"]; // gives you title as string
}
Update :
NameValueCollection nvc = Request.Form;
string userName, password;
if (!string.IsNullOrEmpty(nvc["txtUserName"]))
{
userName = nvc["txtUserName"];
}
if (!string.IsNullOrEmpty(nvc["txtPassword"]))
{
password = nvc["txtPassword"];
}
Try Request.Params, it should contain all GET and/or POST parameters, Request.Form should contain only form parameters.

ASP.NET : Hide Querystring in URL

I don't know if I'm just being overly hopeful, but is there a way to hide the query string returned in the URL?
The scenario I am in is where I have page1.aspx redirecting a command to an outside server via a post, and it returns it to page2.aspx. The only problem I have with this, is that the querystring of the returned variables are still left in the URL.
I just want to hide the ugly string/information from the common user. So is there a way to edit and reload that in the pageload method or do I just have to save the variables on a middleman page and then hit page 2.
What is the origin of these querystring variables? Can you not submit all data as POST data, so that there is no querystring?
You could possibly also use
Context.RewritePath("/foo.aspx")
Here's a link to a ScottGu blog post about URL rewriting.
http://weblogs.asp.net/scottgu/archive/2007/02/26/tip-trick-url-rewriting-with-asp-net.aspx
Awhile back I made some http encoding encrypt/decrypt methods for this purpose. Sometimes in asp.net you need to use the query string, but you also need the end user to not know the value. What I do is base 64 encode, encrypt the value, hash the value based on my private key, and stick them together with a -. On the other side I check the left side hash to verify authenticity, and decrypt the right side. One really nice gotcha is that + (which is a valid base64 string value) is equal to space in html encoding, so I take that into account in the decrypt.
The way I use this is add the encrypted value to the query string, and then decrypt it on the other side
private const string KEY = "<random value goes here>";
public static string EncryptAndHash(this string value)
{
MACTripleDES des = new MACTripleDES();
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
des.Key = md5.ComputeHash(Encoding.UTF8.GetBytes(KEY));
string encrypted = Convert.ToBase64String(des.ComputeHash(Encoding.UTF8.GetBytes(value))) + '-' + Convert.ToBase64String(Encoding.UTF8.GetBytes(value));
return HttpUtility.UrlEncode(encrypted);
}
/// <summary>
/// Returns null if string has been modified since encryption
/// </summary>
/// <param name="encoded"></param>
/// <returns></returns>
public static string DecryptWithHash(this string encoded)
{
MACTripleDES des = new MACTripleDES();
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
des.Key = md5.ComputeHash(Encoding.UTF8.GetBytes(KEY));
string decoded = HttpUtility.UrlDecode(encoded);
// in the act of url encoding and decoding, plus (valid base64 value) gets replaced with space (invalid base64 value). this reverses that.
decoded = decoded.Replace(" ", "+");
string value = Encoding.UTF8.GetString(Convert.FromBase64String(decoded.Split('-')[1]));
string savedHash = Encoding.UTF8.GetString(Convert.FromBase64String(decoded.Split('-')[0]));
string calculatedHash = Encoding.UTF8.GetString(des.ComputeHash(Encoding.UTF8.GetBytes(value)));
if (savedHash != calculatedHash) return null;
return value;
}
I don't like this approach, but it will work.
Once you know you are where you need to be you can Response.Redirect to the same page and they will be gone.
It preserves Query String and Form Variables (optionally). It doesn’t show the real URL where it redirects the request in the users web browser. Server.Transfer happens without the browser knowing anything. The browser requests a page, but the server returns the content of another.
protected void btnServer_Click(object sender, EventArgs e)
{
Server.Transfer("~/About.aspx?UserId=2");
}

Retrieval of input type=password Request.Form["strPassword"] gives null

Are there any special considerations trying to read up data from an HTML form where the element is an input type="Password"? When a ColdFusion page POSTs my handler with form data I am getting null for the password instead of the typed value.
Here is the key line from the larger block below:
string password = context.Request.Form["strPassword"];
I have an HTTPHandler.ashx code file that performs an upload of a file when posted. Here is the key snippet of this code:
string username = context.Request.Form["strUsername"];
if (String.IsNullOrEmpty(username))
{
IdentifyInvoker = GetUserInfo();
brokerService = new Broker.FileService();
}
else
{
string password = context.Request.Form["strPassword"];
string domain = context.Request.Form["strDomain"];
IdentifyInvoker = GetInvokerInfoFromForm(username, password, domain);
brokerService = new Broker.FileService(username,password,domain);
}
The form from which the above code is posted (from ColdFusion) looks like this:
<b>User Name</b> <input type="text" name="strUsername" id="strUsername" size="13" />
<b>Password</b> <input type="Password" name="strPassword" id="strPassword" size="15" />
<b>Domain</b> <input type="text" name="strDomain" id="strDomain" size="13" value="cbmiweb" />
I was able to trap this with the debugger and was shocked to see that after this:
string password = context.Request.Form["strPassword"];
... password = null
In the immediate window, sure enough:
?context.Request.Form["strPassword"]
null
If I examine the entire Form collection in the debugger, I see the proper values laid out (separated by &) and none of the important data elements is null (but strangely the data contains a plus sign in front of the equal sign)! Here is a snippet from the immed window:
&strUsername=johna&strPassword+=xxxxxxxx&strDomain+=cbmiweb}
I have an ASP.NET client that POSTs to this same HTTPHandler and that works fine. Here the same form data shows without the interfering PLUS signs:
&strUsername=johna&strPassword=xxxxxxxx&strDomain=cbmiweb}
Any ideas on what causes this and how to retrieve the form data when it's formatted with the intervening PLUS signs?
EDIT:
Both the ASP.NET form and the ColdFusion form specify enctype="multipart/form-data" yet the latter embeds these PLUS signs.
Plus sign is the problem, it should not have been there, is your coldfusion forwarding request to your page or it is using its internal http request engine to do so?
Plus sign appears due to white space, please check in your coldfusion if any string concatenation caused white spaces to be inserted in your posted data?

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;
}

Resources