How do I prevent Flash's URLRequest from escaping the url? - apache-flex

I load some XML from a servlet from my Flex application like this:
_loader = new URLLoader();
_loader.load(new URLRequest(_servletURL+"?do=load&id="+_id));
As you can imagine _servletURL is something like http://foo.bar/path/to/servlet
In some cases, this URL contains accented characters (long story). I pass the unescaped string to URLRequest, but it seems that flash escapes it and calls the escaped URL, which is invalid. Ideas?

My friend Luis figured it out:
You should use encodeURI does the UTF8URL encoding
http://livedocs.adobe.com/flex/3/langref/package.html#encodeURI()
but not unescape because it unescapes to ASCII see
http://livedocs.adobe.com/flex/3/langref/package.html#unescape()
I think that is where we are getting a %E9 in the URL instead of the expected %C3%A9.
http://www.w3schools.com/TAGS/ref_urlencode.asp

I'm not sure if this will be any different, but this is a cleaner way of achieving the same URLRequest:
var request:URLRequest = new URLRequest(_servletURL)
request.method = URLRequestMethod.GET;
var reqData:Object = new Object();
reqData.do = "load";
reqData.id = _id;
request.data = reqData;
_loader = new URLLoader(request);

From the livedocs: http://livedocs.adobe.com/flex/3/langref/flash/net/URLRequest.html
Creates a URLRequest object. If System.useCodePage is true, the request is encoded using the system code page, rather than Unicode. If System.useCodePage is false, the request is encoded using Unicode, rather than the system code page.
This page has more information: http://livedocs.adobe.com/flex/3/html/help.html?content=18_Client_System_Environment_3.html
but basically you just need to add this to a function that will be run before the URLRequest (I would probably put it in a creationComplete event)
System.useCodePage = false;

Related

Decode special characters when parsing html mvc

In my mvc web application I am trying to parse a html document. It seems to work fine but the only issue is that it gives me special charters and not parse characters like æ,å,ø etc correctly.
Here is my code
var html = new HtmlDocument();
html.LoadHtml(new WebClient().DownloadString("http://cricketforbundet.no/index.php/en/klubber"));
var root = html.DocumentNode;
var p = root.Descendants("table").FirstOrDefault().Descendants("tr").Skip(1).FirstOrDefault().ChildNodes.Where(i=>i.Name == "td").FirstOrDefault().InnerText;
I get Bjørvika Cricket Klubb in p where as I should get Bjørvika Cricket Klubb.
Any thoughts? I am using HtmlAgilityPack to parse HTML in ASP.NET
You have to use load instead of LoadHtml and make sure use UTF8 encoding
WebClient webClient = new WebClient();
HtmlDocument html = new HtmlDocument();
html.Load(webClient.OpenRead("http://cricketforbundet.no/index.php/en/klubber"), Encoding.UTF8);
var root = html.DocumentNode;
var p = root.Descendants("table").FirstOrDefault().Descendants("tr").Skip(1).FirstOrDefault().ChildNodes.Where(i => i.Name == "td").FirstOrDefault().InnerText;
check this answer

Modify request querystring parameters to build a new link without resorting to string manipulation

I want to dynamically populate a link with the URI of the current request, but set one specific query string parameter. All other querystring paramaters (if there are any) should be left untouched. And I don't know in advance what they might be.
Eg, imagine I want to build a link back to the current page, but with the querystring parameter "valueOfInterest" always set to be "wibble" (I'm doing this from the code-behind of an aspx page, .Net 3.5 in C# FWIW).
Eg, a request for either of these two:
/somepage.aspx
/somepage.aspx?valueOfInterest=sausages
would become:
/somepage.aspx?valueOfInterest=wibble
And most importantly (perhaps) a request for:
/somepage.aspx?boring=something
/somepage.aspx?boring=something&valueOfInterest=sausages
would preserve the boring params to become:
/somepage.aspx?boring=something&valueOfInterest=wibble
Caveats: I'd like to avoid string manipulation if there's something more elegant in asp.net that is more robust. However if there isn't something more elegant, so be it.
I've done (a little) homework:
I found a blog post which suggested copying the request into a local HttpRequest object, but that still has a read-only collection for the querystring params. I've also had a look at using a URI object, but that doesn't seem to have a querystring
This will work as long as [1] you have a valid URL to begin with (which seems reasonable) [2] you make sure that your new value ('sausages') is properly escaped. There's no parsing, the only string manipulation is to concatenate the parameters.
Edit
Here's the C#:
UriBuilder u = new UriBuilder(Request.Url);
NameValueCollection nv = new NameValueCollection(Request.QueryString);
/* A NameValueColllection automatically makes room if this is a new
name. You don't have to check for NULL.
*/
nv["valueOfInterest"] = "sausages";
/* Appending to u.Query doesn't quite work, it
overloaded to add an extra '?' each time. Have to
use StringBuilder instead.
*/
StringBuilder newQuery = new StringBuilder();
foreach (string k in nv.Keys)
newQuery.AppendFormat("&{0}={1}", k, nv[k]);
u.Query = newQuery.ToString();
Response.Redirect(u.Uri.ToString());
UriBuilder u = new UriBuilder(Request.Url);
NameValueCollection nv = new NameValueCollection(Request.QueryString);
nv["valueofinterest"] = "wibble";
string newQuery = "";
foreach (string k in nv.Keys)
{
newQuery += k + "=" + nv[k] + "&";
}
u.Query = newQuery.Substring(0,newQuery.Length-1);
Response.Redirect(u.ToString());
that should do it
If you can't find something that exists to do it, then build a bullet-proof function to do it that is thoroughly tested and can be relied upon. If this uses string manipulation, but is efficient and fully tested, then in reality it will be little different to what you may find any way.

NVelocity not finding the template

I'm having some difficulty with using NVelocity in an ASP.NET MVC application. I'm using it as a way of generating emails.
As far as I can make out the details I'm passing are all correct, but it fails to load the template.
Here is the code:
private const string defaultTemplatePath = "Views\\EmailTemplates\\";
...
velocityEngine = new VelocityEngine();
basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, defaultTemplatePath);
ExtendedProperties properties = new ExtendedProperties();
properties.Add(RuntimeConstants.RESOURCE_LOADER, "file");
properties.Add(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, basePath);
velocityEngine.Init(properties);
The basePath is the correct directory, I've pasted the value into explorer to ensure it is correct.
if (!velocityEngine.TemplateExists(name))
throw new InvalidOperationException(string.Format("Could not find a template named '{0}'", name));
Template result = velocityEngine.GetTemplate(name);
'name' above is a valid filename in the folder defined as basePath above. However, TemplateExists returns false. If I comment that conditional out and let it fail on the GetTemplate method call the stack trace looks like this:
at NVelocity.Runtime.Resource.ResourceManagerImpl.LoadResource(String resourceName, ResourceType resourceType, String encoding)
at NVelocity.Runtime.Resource.ResourceManagerImpl.GetResource(String resourceName, ResourceType resourceType, String encoding)
at NVelocity.Runtime.RuntimeInstance.GetTemplate(String name, String encoding)
at NVelocity.Runtime.RuntimeInstance.GetTemplate(String name)
at NVelocity.App.VelocityEngine.GetTemplate(String name)
...
I'm now at a bit of an impasse. I feel that the answer is blindingly obvious, but I just can't seem to see it at the moment.
Have you considered using Castle's NVelocityTemplateEngine?
Download from the "TemplateEngine Component 1.1 - September 29th, 2009" section and reference the following assemblies:
using Castle.Components.Common.TemplateEngine.NVelocityTemplateEngine;
using Castle.Components.Common.TemplateEngine;
Then you can simply call:
using (var writer = new StringWriter())
{
_templateEngine.Process(data, string.Empty, writer, _templateContents);
return writer.ToString();
}
Where:
_templateEngine is your NVelocityTemplateEngine
data is your Dictionary of information (I'm using a Dictionary to enable me to access objects by a key ($objectKeyName) in my template.
_templateContents is the actual template string itself.
I hope this is of help to you!
Just to add, you'll want to put that into a static method returning a string of course!
Had this issue recently - NVelocity needs to be initialised with the location of the template files. In this case mergeValues is an anonymous type so in my template I can just refer to $Values.SomeItem:
private string Merge(Object mergeValues)
{
var velocity = new VelocityEngine();
var props = new ExtendedProperties();
props.AddProperty("file.resource.loader.path", #"D:\Path\To\Templates");
velocity.Init(props);
var template = velocity.GetTemplate("MailTemplate.vm");
var context = new VelocityContext();
context.Put("Values", mergeValues);
using (var writer = new StringWriter())
{
template.Merge(context, writer);
return writer.ToString();
}
}
Try setting the file.resource.loader.path
http://weblogs.asp.net/george_v_reilly/archive/2007/03/06/img-srchttpwwwcodegenerationnetlogosnveloc.aspx
Okay - So I'm managed to get something working but it is a bit of a hack and isn't anywhere near a solution that I want, but it got something working.
Basically, I manually load in the template into a string then pass that string to the velocityEngine.Evaluate() method which writes the result into the the given StringWriter. The side effect of this is that the #parse instructions in the template don't work because it still cannot find the files.
using (StringWriter writer = new StringWriter())
{
velocityEngine.Evaluate(context, writer, templateName, template);
return writer.ToString();
}
In the code above templateName is irrelevant as it isn't used. template is the string that contains the entire template that has been pre-loaded from disk.
I'd still appreciate any better solutions as I really don't like this.
The tests are the ultimate authority:
http://fisheye2.atlassian.com/browse/castleproject/NVelocity/trunk/src/NVelocity.Tests/Test/ParserTest.cs?r=6005#l122
Or you could use the TemplateEngine component which is a thin wrapper around NVelocity that makes things easier.

Get URL of current page from Flex 3?

How do I determine the URL of the current page from within Flex?
Let's be clear here.
1. If you want the URL of the loaded SWF file, then use one of these.
Inside your application:
this.url;
From anywhere else:
Application.application.url; // Flex 3
FlexGlobals.topLevelApplication.url; // Flex 4
If you are loading your SWF inside another SWF, then keep in mind that the code above will give different values. this.url will return the url of your SWF, where as Application.application.url will give the url of the parent/root SWF.
2. If you want to know the URL that is in the browser address bar, then use one of these.
BrowserManager method(Make sure you have the History.js included in your wrapper html for this to work):
var browser:IBrowserManager = BrowserManager.getInstance();
browser.init();
var browserUrl:String = browser.url; // full url in the browser
var baseUrl:String = browser.base; // the portion of the url before the "#"
var fragment:String = browser.fragment; // the portion of the url after the "#"
JavaScript method:
var browserUrl:String = ExternalInterface.call("eval", "window.location.href");
If you are parsing the url for parameters, don't forget about this useful function:
// parses a query string like "key=value&another=true" into an object
var params:Object = URLUtil.stringToObject(browserURL, "&");
From the Application:
var myUrl:String = Application.application.url;
I searched and came up with this url. I've honestly never used Flex, but it looks like the important part of that document is this:
private function showURLDetails(e:BrowserChangeEvent):void {
var url:String = browserManager.url;
baseURL = browserManager.base;
fragment = browserManager.fragment;
previousURL = e.lastURL;
fullURL = mx.utils.URLUtil.getFullURL(url, url);
port = mx.utils.URLUtil.getPort(url);
protocol = mx.utils.URLUtil.getProtocol(url);
serverName = mx.utils.URLUtil.getServerName(url);
isSecure = mx.utils.URLUtil.isHttpsURL(url);
}
Either way, good luck! :)
Using ExternalInterface (flash.external.ExternalInterface), you can execute Javascript in the browser.
Knowing this, you can call
ExternalInterface.call("window.location.href.toString");
to get the current URL (note that this will be the page url and not the .swf url).
hth
Koen
From the Application, use: this.loaderInfo.loaderURL
to break it apart and use parts of it do:
var splitURL:Array = this.loaderInfo.loaderURL.split('/');
var baseURL:String = "http://"+splitURL[2];
I tried the e:BrowserChangeEvent version and inside my class it didnt or wasnt the appropriate moment for this event to work so in short it didn't work !
Using Application.application.loaderInfo.loaderURL is my preferred solution.
ExternalInterface.call("window.location.href.toString");

Flex 3 - how to support HTTP Authentication URLRequest?

I have a Flex file upload script that uses URLRequest to upload files to a server. I want to add support for http authentication (password protected directories on the server), but I don't know how to implement this - I assume I need to extend the class somehow, but on how to I'm a little lost.
I tried to modify the following (replacing HTTPService with URLRequest), but that didn't work.
private function authAndSend(service:HTTPService):void{
var encoder:Base64Encoder = new Base64Encoder();
encoder.encode("someusername:somepassword");
service.headers = {Authorization:"Basic " + encoder.toString()};
service.send();
}
I should point out that I'm not knowledgeable when it comes to ActionScript / Flex, although I have managed to successfully modify the upload script somewhat.
[Edit] - here is an update of my progress, based on the answer below, although I still cannot get this to work:
Thank you for your assistance. I've tried to implement your code but I've not had any luck.
The general behaviour I'm experiencing when dealing with HTTP authenticated locations is that with IE7 all is well but in Firefox when I attempt to upload a file to the server it displays an HTTP authentication prompt - which even if given the correct details, simply stalls the upload process.
I believe the reason IE7 is ok is down to the the session / authentication information being shared by the browser and the Flash component - however, in Firefox this is not the case and I experience the above behaviour.
Here is my updated upload function, incorporating your changes:
private function pergress():void
{
if (fileCollection.length == 0)
{
var urlString:String = "upload_process.php?folder="+folderId+"&type="+uploadType+"&feid="+formElementId+"&filetotal="+fileTotal;
if (ExternalInterface.available)
{
ExternalInterface.call("uploadComplete", urlString);
}
}
if (fileCollection.length > 0)
{
fileTotal++;
var urlRequest:URLRequest = new URLRequest("upload_file.php?folder="+folderId+"&type="+uploadType+"&feid="+formElementId+"&obfuscate="+obfuscateHash+"&sessidpass="+sessionPass);
urlRequest.method = URLRequestMethod.POST;
urlRequest.data = new URLVariables("name=Bryn+Jones");
var encoder:Base64Encoder = new Base64Encoder();
encoder.encode("testuser:testpass");
var credsHeader:URLRequestHeader = new URLRequestHeader("Authorization", "Basic " + encoder.toString());
urlRequest.requestHeaders.push(credsHeader);
file = FileReference(fileCollection.getItemAt(0));
file.addEventListener(Event.COMPLETE, completeHandler);
file.addEventListener(HTTPStatusEvent.HTTP_STATUS, onHTTPStatus);
file.addEventListener(ProgressEvent.PROGRESS, onUploadProgress);
file.upload(urlRequest);
}
}
As stated above, I seem to be experiencing the same results with or without the amendments to my function.
Can I ask also where the crossdomain.xml should be located - as I do not currently have one and am unsure where to place it.
The syntax is a little different for URLRequest, but the idea's the same:
private function doWork():void
{
var req:URLRequest = new URLRequest("http://yoursite.com/yourservice.ext");
req.method = URLRequestMethod.POST;
req.data = new URLVariables("name=John+Doe");
var encoder:Base64Encoder = new Base64Encoder();
encoder.encode("yourusername:yourpassword");
var credsHeader:URLRequestHeader = new URLRequestHeader("Authorization", "Basic " + encoder.toString());
req.requestHeaders.push(credsHeader);
var loader:URLLoader = new URLLoader();
loader.load(req);
}
A couple of things to keep in mind:
Best I can tell, for some reason, this only works where request method is POST; the headers don't get set with GET requests.
Interestingly, it also fails unless at least one URLVariables name-value pair gets packaged with the request, as indicated above. That's why many of the examples you see out there (including mine) attach "name=John+Doe" -- it's just a placeholder for some data that URLRequest seems to require when setting any custom HTTP headers. Without it, even a properly authenticated POST request will also fail.
Apparently, Flash player version 9.0.115.0 completely blocks all Authorization headers (more information on this one here), so you'll probably want to keep that in mind, too.
You'll almost surely have to modify your crossdomain.xml file to accommodate the header(s) you're going to be sending. In my case, I'm using this, which is a rather wide-open policy file in that it accepts from any domain, so in your case, you might want to limit things a bit more, depending on how security-conscious you are.
crossdomain.xml:
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*" />
<allow-http-request-headers-from domain="*" headers="Authorization" />
</cross-domain-policy>
... and that seems to work; more information on this one is available from Adobe here).
The code above was tested with Flash player 10 (with debug & release SWFs), so it should work for you, but I wanted to update my original post to include all this extra info in case you run into any issues, as the chances seem (sadly) likely that you will.
Hope it helps! Good luck. I'll keep an eye out for comments.
The FileReference.upload() and FileReference.download() methods do not support the URLRequest.requestHeaders parameter.
http://livedocs.adobe.com/flex/2/langref/flash/net/URLRequest.html
If you want to upload a file, you just need to send the correct headers and the content of file using URLRequest via UploadPostHelper class. This works 100%, i am using this class to upload generated images and CSV files, but you could upload any kind of file.
This class simply prepares the request with headers and content as if you would be uploading the file from a html form.
http://code.google.com/p/as3asclublib/source/browse/trunk/net/UploadPostHelper.as?r=118
_urlRequest = new URLRequest(url);
_urlRequest.data = "LoG";
_urlRequest.method = URLRequestMethod.POST;
_urlRequest.requestHeaders.push(new URLRequestHeader("X-HTTP-Code-Override", "true"));
_urlRequest.requestHeaders.push(new URLRequestHeader("pragma", "no-cache"));
initCredentials();
_loader.dataFormat = URLLoaderDataFormat.BINARY;
//this creates a security problem, putting the content type in the headers bypasses this problem
//_urlRequest.contentType = 'multipart/form-data; boundary=' + UploadPostHelper.getBoundary();
_urlRequest.requestHeaders.push( new URLRequestHeader( 'Cache-Control', 'no-cache' ) );
_urlRequest.requestHeaders.push(new URLRequestHeader('Content-Type', 'multipart/form-data; boundary=' + UploadPostHelper.getBoundary()));
_urlRequest.data = UploadPostHelper.getPostData("file.csv", param[1]);
_loader.load(_urlRequest);
I'm not sure about this but have you tried adding username:password# to the beginning of your url?
"http://username:password#yoursite.com/yourservice.ext"
var service : HTTPService = new HTTPService ();
var encoder:Base64Encoder = new Base64Encoder();
encoder.insertNewLines = false;
encoder.encode("user:password");
service.headers = {Authorization:"Basic " + encoder.toString()};
service.method = HTTPRequestMessage.POST_METHOD;
service.request = new URLVariables("name=John+Doe");
service.addEventListener(FaultEvent.FAULT,error_handler );
service.addEventListener(ResultEvent.RESULT,result_handler);
service.url = 'http://blah.blah.xml?'+UIDUtil.createUID();
service.send();
Seemingly similar problem was solved here. I urge you to also check the Flexcoders post linked to in the first post.
The problem was that FireFox uses a separate browser window instance to send the file upload request. The solution is to manually attach the session id to the request url. The session id is not attached as a regular GET variable, but with a semicolon (the reason for this syntax is unknown to me).
Flash is very limited in terms of what sort of headers you can pass with an http request (and it changes between browsers and OSes). If you get this to work on one browser/OS, make sure you test it on the others.
The best thing to do is not mess with HTTP headers.
We have the same issue (uploading to Picasa Web Albums from flash) and post through a proxy on our server. We pass the extra headers through as post parameters and our proxy does the right thing.
"http://username:password#yoursite.com/yourservice.ext"
This doesn't work in IE (http://www.theregister.co.uk/2004/01/30/ms_drop_authentication_technique/) and doesn't seem to work in Chrome either.
probably not usable in Flash
Here is a work-around when using ASP.Net based in part on the work here.
I built a component that dynamically writes Flex objects to the page so they can be used in UpdatePanels. Message me if you want they code. To solve the above problem in pages where authentication cookies will need to be sent by URLRequest, I add the values in as flashVars.
This code only works in my object, but you get the idea
Dictionary<string, string> flashVars = new Dictionary<string, string>();
flashVars.Add("auth", Request.Cookies["LOOKINGGLASSFORMSAUTH"].Value);
flashVars.Add("sess", Request.Cookies["ASP.NET_SessionId"].Value);
myFlexObject.SetFlashVars(flashVars);
Then in the Flex Object, check for the params
if (Application.application.parameters.sess != null)
sendVars.sess= Application.application.parameters.sess;
if (Application.application.parameters.auth != null)
sendVars.au= Application.application.parameters.auth;
request.data = sendVars;
request.url = url;
request.method = URLRequestMethod.POST;
Finally stuff the cookies in on global.asax BeginRequest
if (Request.RequestType=="POST" && Request.Path.EndsWith("upload.aspx"))
{
try
{
string session_param_name = "sess";
string session_cookie_name = "ASP.NET_SESSIONID";
string session_value = Request.Form[session_param_name]; // ?? Request.QueryString[session_param_name];
if (session_value != null) { UpdateCookie(session_cookie_name, session_value); }
}
catch (Exception) { }
try
{
string auth_param_name = "au";
string auth_cookie_name = FormsAuthentication.FormsCookieName;
string auth_value = Request.Form[auth_param_name];// ?? Request.QueryString[auth_param_name];
if (auth_value != null) { UpdateCookie(auth_cookie_name, auth_value); }
}
catch (Exception) { }
}
Hope this help someone avoid the 6 hours I just spent addressing this. Adobe has closed the issue as unresolvable, so this was my last resort.

Resources