Generate XML -- client-side or server-side?


  • Discourse touched me in a no-no place

    Let's say I have a page that needs to communicate with a web service on another server--specifically, I need to send it some XML and then read & parse the result. What makes more sense? Generating some kind of embedded XML on the server, and then, say, on the client, stuffing that through AJAX to the other server? Or (say) generating JSON on the server, and on the client, using js to build XML and then AJAX it? I've only ever done something like this once, and I came up with a solution that won't apply here for reasons too boring to get into.



  • So basically it is server-to-server via client?

    I'd say server A builds digitally signed XML to client that passes-through to server B. Assuming both server nodes can trust each-other.


  • Discourse touched me in a no-no place

    @MathNerdCNU said:

    So basically it is server-to-server via client?

    Yes.

    @MathNerdCNU said:

    I'd say server A builds digitally signed XML to client that passes-through to server B. Assuming both server nodes can trust each-other.

    It's a public-ish API. Well, API may be too strong a word. I have to send XML, retrieve the result, and look for the presence of a string in the response. After that I'll have to do something else, which might be "redirect to another URL."



  • @FrostCat said:

    using js to build XML

    I'm a little confused on the flow of the data and what is needed where, but if it were me I would never build xml in js :-)



  • I'm going to go left field for some of this so bear with me.

    Assuming that the client has a temporarily issued x509 certs:

    Server-A issues a message in XML to Client.

    Client validates Sever-A and does some trans-fig-morgi-action-that results in a modified message.Signed with Client's temp cert.

    Client sends response to Server-B who validates the "temp" cert(assume a digitally signed message from server-A to server-B happened out of band to ensure this scenario). Server-b validates the cert on message from Client and then accepts.

    So basically I assume digitally signed-XML is easier than JSON?


  • Discourse touched me in a no-no place

    @monkeyArms said:

    if it were me I would never build xml in js

    Well, I mean, it could be as crude as var x = "<?xml .. >". The XML involved isn't terribly large or complex in either direction. Think stuff on the level of demographics data or...actually, trying to embed data from Google Maps via the API in a web page would probably come close to what I'm looking for, in functionality if not in the exact methodology.


  • Grade A Premium Asshole

    I would say that the generating client builds the XML that is passed to the receiving server, if you are in control of the client side, or can truly dictate the client side hierarchy. Either way, you need a mechanism to notify the client of failures. Do it client side when you can, it saves on your overhead.


  • Discourse touched me in a no-no place

    @MathNerdCNU said:

    So basically I assume digitally signed-XML is easier than JSON?

    The documents I have say nothing about signing anything. "Send some XML to me at <url>, and I'll send you some back." I was originally thinking of doing that on the server, but I'm now leaning to client-side AJAX, as I said. That would probably be easier.


  • Discourse touched me in a no-no place

    @Polygeekery said:

    I would say that the generating client builds the XML that is passed to the receiving server, if you are in control of the client side, or can truly dictate the client side hierarchy. Either way, you need a mechanism to notify the client of failures. Do it client side when you can, it saves on your overhead.

    I'm not sure quite what you meant by "dictate the client side hierarchy." This is actually new functionality for my company's existing web application. We're integrating someone else's product into our own--supposedly it can be made to more or less look like it's a seamless part of ours, usually by working in an iframe, they tell me.


  • Grade A Premium Asshole

    @FrostCat said:

    I'm not sure quite what you meant by "dictate the client side hierarchy."

    Well, that would fall under the auspice of you writing a client that consumes their information and feeds it to your service. Or, less ideal, you tell them that "you write a service that converts it to this format of XML or we will just dump it to /dev/null".

    @FrostCat said:

    We're integrating someone else's product into our own

    Good...fucking...luck. Now you have to make their technological debt mesh with your technological debt. I do not envy you in the slightest. ;)

    @FrostCat said:

    supposedly it can be made to more or less look like it's a seamless part of ours

    Emphasis mine. I wish you luck, and it might not be a total bastard, but "standardized data interchange formats" are frequently none of the above.



  • @FrostCat said:

    supposedly it can be made to more or less look like it's a seamless part of ours, usually by working in an iframe

    Super extra triple good luck.


  • Grade A Premium Asshole

    @monkeyArms said:

    Super extra triple good luck.

    What he said.


  • Discourse touched me in a no-no place

    Ok, so I have made a bit of headway. I'm basically using the sample AJAX you can find from w3schools: set up an XMLHttpRequest, set an event handler for onreadystate(), and then post data like this:

    xmlhttp.open("POST",url,true);
    xmlhttp.send(data);

    The data is a block of XML (for now I'm literally using their sample). All I have for instructions from the "documentation" is basically "post the following XML to [our URL]." I mean literally it's like one sentence. When I do that, I get back a 400 error, and I am not actually sure what's wrong. I did try URL encoding the data, and setting the POST content-type to application/x-www-form-urlencoded or whatever the exact string is if that's not it, and that didn't help. Is there anything obvious I'm missing?

    I have used AJAX successfully in the past, but only a fairly trivial--ok, slightly more trivial than what I described above--amount, to get some JSON to put some dynamic stuff on the page.


  • Discourse touched me in a no-no place

    Ugh. Problem solved--I forgot to send the content length.


  • Discourse touched me in a no-no place

    Problem de-solved. Apparently, you can't set the Content-Length header in an xhr via script? Web pages say the browser will set that for you, but Fiddler disagrees. I've got XML in a string, and I use xhr.open("POST", url, true); xhr.setContentHeader() to set the Content-Type and Content-Length (which you apparently can't do, in spite of a bunch of examples saying otherwise), and then xhr.send(xmlvar) and the server responds 400 because it's not getting a content length. What gives?

    I had thought it might be an IE-ism, so I tried Chrome. No joy. I read something that suggested it won't work with localhost, so try the machine name. No joy. Then I tried using a different server, and it still didn't take. How do I get it to work?


  • SockDev

    ..... do you get the length header when you don't set the content type?

    what about if you pull something like jquery into the mix and send the XHR with it (just to isolate the issue of course)



  • Is a browser always going to be the client? If so, you probably can't set the content-length header yourself. It's apparently a security feature (per SO).


  • Discourse touched me in a no-no place

    @accalia said:

    do you get the length header when you don't set the content type?

    As far as I can tell, it's 0 bytes whether I set it or not. dev.mozilla.org or whatever, and I think the w3c say something like "quit processing headers if you find [a list of headers including content-length] being set."

    @accalia said:

    what about if you pull something like jquery into the mix and send the XHR with it

    I could probably do that. I was trying to get a basic feel for how to interface with this library first, though.


  • Discourse touched me in a no-no place

    @rad131304 said:

    Is a browser always going to be the client?

    Yes. Based on my experience, unless I was doing something wrong, "There's no need or reason to try to set the request length, as the browser can do that accurately from the length of data you pass to send()." is a lie.



  • http://jsfiddle.net/rad131304/4jx909bh/4/

    Content Length seems to set itself just fine for me in Chrome[latest stable] and IE11. Must be a loopback thing.

    Edit: the fiddle doesn't show it, but when i tried setting content-length, the Pure JS version throws an error saying it's not safe.



  • It's probably a silly question, but have you made sure what you actually set the content length to?

    Seriously, though. Use jQuery. That's one of the two things it's good for.


  • Discourse touched me in a no-no place

    @rad131304 said:

    Content Length seems to set itself just fine for me in Chrome[latest stable] and IE11. Must be a loopback thing.

    I tried using localhost, and then the machine's name instead, because I read something that suggested that localhost wouldn't work. But I also tried working against a different server, too, and IIRC I did all three of these things with two different browsers.


  • Discourse touched me in a no-no place

    @Maciejasjmj said:

    It's probably a silly question, but have you made sure what you actually set the content length to?

    Yeah. I put an alert in.


  • Discourse touched me in a no-no place

    @Maciejasjmj said:

    Seriously, though. Use jQuery. That's one of the two things it's good for.

    I guess I'll have to try it--or rather, figure out how to do it. What I've seen of the jQuery docs didn't impress me.

    Edit: Also, fuck you Discourse toaster, this is my thread, I'll post in it as much as I want.



  • Minimal example:

    
    <script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
    
    <script>
    
    (function()
    {
        var url = 'https://example.com',
            xml = '<foo>bar</foo>';
            
        
        $.ajax({
            type:            'POST',
            url:            url,
            dataType:        'xml',
            contentType:    "application/xml",
            data:            xml,
            success: function( result )
            {
                // at least the request worked....
                console.log( result );
            },
            error: function( result )
            {
                // shit damn fuck
                console.log( result );
            }
        });
        
    })();
    
    </script>
    
    

  • Discourse touched me in a no-no place

    Thanks, that'll save me some work.



  • @FrostCat said:

    I tried using localhost, and then the machine's name instead, because I read something that suggested that localhost wouldn't work. But I also tried working against a different server, too, and IIRC I did all three of these things with two different browsers.

    It depends what the server thinks the host name it's supposed to respond to API requests on should be; cross domain requests are usually blocked to prevent CSRF attacks, especially when the Content-Type set improperly.

    Come to think of it, your Content-Type is probably causing the request to be rejected at the server. you'll have to figure out what the proper request type is:

    1. set the Content-Type header to that avalue
    2. (optionally) set the X-Requested-With header to XMLHttpRequest

    updated fiddle

    Edit: fixed my backwards-ass remembering of how x-www-form-urlencoded blocks CSRF ....



  • Ok, my previous response was about as clear as mud - what I was trying to say is that the servers you are using to test are probably rejecting the AJAX request because the headers defined the request as cross-origin request (i.e. the server responds to requests at example.com, and it sees the request as coming from other.com).

    You have two options:

    1. open the server to cross-origin requests (see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS)
    2. change the requesting source to be in the same-origin

    I'm not sure which would be easier for you.


  • Discourse touched me in a no-no place

    @rad131304 said:

    set the Content-Type header to that avalue

    I'm setting it to application/xml, which the server will take, when I use telnet and hand-crafted requests. I saw the jQuery sample above just before lunch and saved it; I'm going to try doing that now. Once I have it working in any realistic form at all, it was almost certainly destined to use jQuery anyway.

    Regarding your point 2, I'll remember that and try it if I need to. IIRC in my initial tests, the server was actually lax on the content-type it accepted (but I could be wrong so I'll double-check it) and the key thing it was after was the content length. I'd love to know why the browsers weren't setting it. I may ask on SO at some point, to see if anyone can shed some light.


  • Discourse touched me in a no-no place

    @rad131304 said:

    Ok, my previous response was about as clear as mud - what I was trying to say is that the servers you are using to test are probably rejecting the AJAX request because the headers defined the request as cross-origin request (i.e. the server responds to requests at example.com, and it sees the request as coming from other.com).

    I understand what you are saying but it's actually the same server at the moment. Right now, I have a tomcat application in .war form and a couple of static pages. (I was thinking about trying to pull XML from the server via ajax, rather than build it client-side, and then submit that same XML to the tomcat application.
    Once I can make this work, the next step is to integrate this into our existing web application. At that point, there will be two different web servers, but they'll both be hosted on our TLD. They may or may not be on separate subdomains in production but I don't know at this time. So I will remember the cross-origin stuff. Fortunately I have more than one dev machine available so I can test this early on, so thanks for the forethought.



  • I've only had to tackle CORS once, and then I found that it consists of two parts:

    • Whether the browser will even do the request
    • Whether the browser will accept the response

    Notably, I found chrome will completely refuse to do cross-origin requests if the xhr target is not on the default port (80 or 443). Both chrome and firefox would consider a request to be a CORS request if the domain name or port mismatches the original page; they'll then either do an OPTIONS request first or reject the reply if certain reply headers aren't present.

    IIRC explicitly specifying content-type will trigger a preflighted CORS request; there'll be an initial OPTIONS request which needs to be responded to with appropriate CORS reply headers, or the real request will never be sent.

    To emphasize, the following are all separate origins for CORS purposes:

    • server1.company.com
    • server2.company.com
    • server1.company.com:8080
    • server2.company.com:8080

    Additionally chrome WILL NOT do cors requests to the last two. Ever.


  • Discourse touched me in a no-no place

    @PleegWat said:

    Additionally chrome WILL NOT do cors requests to the last two. Ever.

    This may be a problem for me a step or three down the road. (Of course, my company's just now moving beyond "we target IE5" with this particular product, sigh.)



  • CORS is a bitch. Took me ages to add it to my web API parent class.



  • @FrostCat said:

    I'm setting it to application/xml, which the server will take, when I use telnet and hand-crafted requests. I saw the jQuery sample above just before lunch and saved it; I'm going to try doing that now. Once I have it working in any realistic form at all, it was almost certainly destined to use jQuery anyway.

    Regarding your point 2, I'll remember that and try it if I need to. IIRC in my initial tests, the server was actually lax on the content-type it accepted (but I could be wrong so I'll double-check it) and the key thing it was after was the content length. I'd love to know why the browsers weren't setting it. I may ask on SO at some point, to see if anyone can shed some light.

    FYI: jQuery's default content type for POST requests is application/x-www-form-urlencoded; be sure, like the example, to override it.

    I'm still super puzzled about the setting of the content-length. Setting that value is supposed to be the browser's responsibility so it should not be a Tomcat issue. I can't repo a lack of content-length even when sending the message from/to localhost.

    POST http://localhost/test-ajax.php HTTP/1.1
    Referer: http://localhost/test-ajax.php
    Content-Type: application/xml
    X-Requested-With: XMLHttpRequest
    Accept: application/xml, text/xml, */*; q=0.01
    Accept-Language: en-US
    Accept-Encoding: gzip, deflate
    User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko
    Host: localhost
    Content-Length: 23
    DNT: 1
    Connection: Keep-Alive
    Pragma: no-cache
    
    <?xml?><some>xml</some>
    

    I'm running a Bitnami WAMP stack on a Win 8.1 64 Bit machine

    the php page, sent in Chrome 39 and IE 11, is:

    <?php
    $postdata = file_get_contents("php://input");
    if(strlen($postdata)==0) {
    ?><!DOCTYPE html>
    <html>
    <head></head>
    <body>
    <script>
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "/test-ajax.php");
        xhr.setRequestHeader("Content-Type", "application/xml");
        xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
        xhr.setRequestHeader("Accept", "application/xml, text/xml, */*; q=0.01");
        xhr.onreadystatechange = function() {
            if(xhr.readyState == 4) {
                console.info(xhr);
            }
        }
        xhr.send("<?xml?><some>xml</some>");</script>
    </body>
    </html><?php } else {
    header('Content-Type: application/xml', true)
    echo $postdata;
    }
    

    jquery version:

    <?php
    $postdata = file_get_contents("php://input");
    if(strlen($postdata)==0) {
    ?><!DOCTYPE html>
    <html>
    <head></head>
    <body>
    <script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
    <script>
    	(function(){
    		$.ajax({
    			type:            'POST',
    			url:             '/test-ajax.php',
    			dataType:        'xml',
    			contentType:     'application/xml',
    			data:            '<some>xml</some>'
    		})
    		.then( function done(result, response, xhr) {
    			console.log( result );
    		}, function fail(xhr, response, error) {
    			console.error( error );
    		});
    	})();
    </script>
    </body>
    </html><?php } else {
    header("Content-Type: application/xml", true);
    echo $postdata;
    }
    

  • Discourse touched me in a no-no place

    Here's my really-basic test page. I started fiddling around with some ideas
    [code]

    <!DOCTYPE html> <html> <head> <title></title> <script type="text/javascript"> var xmlhttp; var save; var stage = 0; function body_onload() { if (window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest(); } else { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.onreadystatechange = function () { save = xmlhttp.responseText; if (xmlhttp.readyState == 4) document.getElementById("myDiv").innerHTML = xmlhttp.status; if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { var x; if (stage == 1) { if (window.frames["myif"].contentDocument) { x = window.frames["myif"].contentDocument; } else { x = window.frames["myif"].document; } x.open(); x.write(xmlhttp.responseText); x.close(); } else if (stage == 2) alert(xmlhttp.responseText); } } } </script> </head> <body style="background: aliceblue;" onload="body_onload();">
    <iframe id="myif" width="80%" height="400"></iframe>
    <input type="button" value="raw" id="a" /> <input type="button" value="request" id="b" />
    <script type="text/javascript">
        a.onclick = function doFunction() {
            a.disabled = true;
            stage = 1;
            xmlhttp.open("GET", "ajax.html", true);
            xmlhttp.send();
        }
        b.onclick = function doFunction() {
            stage = 2;
            var sl = save.length;
            xmlhttp.open("POST", "/spf/register", true);
            //xmlhttp.setRequestHeader("Content-Length", sl);
            xmlhttp.setRequestHeader("Content-Type", "application/xml");
            xmlhttp.send(save);
        }
    </script>
    
    </body> </html>[/code]

    Here's what Fiddler says was sent to the server:
    [code]POST http://localhost:8080/spf/register HTTP/1.1
    Host: localhost:8080
    Connection: keep-alive
    Content-Length: 0
    Origin: http://localhost:8080
    User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.85 Safari/537.36
    Content-Type: application/xml
    Accept: /
    Referer: http://localhost:8080/ajaw.html
    Accept-Encoding: gzip, deflate
    Accept-Language: en-US,en;q=0.8
    [/code]

    Maybe I'm missing something else. (In this example, the XML is located in the poorly-named file "ajax.html".)

    I also tried this, as I mentioned above, using my host name instead of localhost; no difference.



  • Is that just Headers or RAW?


  • Discourse touched me in a no-no place

    Raw.



  • have you checked to make sure save isn't empty?


  • Discourse touched me in a no-no place

    Yes--it had an alert displaying sl.length until I got tired of the popup.



  • My watchlist in IE is telling me that save is an empty string.

    EDIT: OOOOOOOOH You're reusing the XHR. It must be overwriting the variable waiting for the new response when you call open.

    Yes that's definitely it, try this:

    <!DOCTYPE html>
    <html>
    <head>
        <title></title>
        <script type="text/javascript">
            var xmlhttp;
            var save;
            var stage = 0;
            function body_onload() {
                if (window.XMLHttpRequest) {
                    xmlhttp = new XMLHttpRequest();
                }
                else {
                    xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
                }
                xmlhttp.onreadystatechange = function () {
                    if (xmlhttp.readyState == 4) { document.getElementById("myDiv").innerHTML = xmlhttp.status; save = xmlhttp.responseText; }
                    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                        var x;
                        if (stage == 1) {
                            if (window.frames["myif"].contentDocument) {
                                x = window.frames["myif"].contentDocument;
                            }
                            else {
                                x = window.frames["myif"].document;
                            }
                            x.open();
                            x.write(xmlhttp.responseText);
                            x.close();
                        }
                        else if (stage == 2)
                            alert(xmlhttp.responseText);
                    }
                }
            }
        </script>
    </head>
    <body style="background: aliceblue;" onload="body_onload();">
        <div style="text-align:center;">
            <div id="myDiv" style="background:gainsboro"></div>
            <iframe id="myif" width="80%" height="400"></iframe>
            <br />
            <input type="button" value="raw" id="a" />
            <input type="button" value="request" id="b" />
        </div>
        <script type="text/javascript">
            a.onclick = function doFunction() {
                a.disabled = true;
                stage = 1;
                xmlhttp.open("GET", "ajax.html", true);
                xmlhttp.send();
            }
            b.onclick = function doFunction() {
                stage = 2;
                var sl = save.length;
                xmlhttp.open("POST", "/spf/register", true);
                //xmlhttp.setRequestHeader("Content-Length", sl);
                xmlhttp.setRequestHeader("Content-Type", "application/xml");
                xmlhttp.send(save);
            }
        </script>
    </body>
    </html>
    

  • Discourse touched me in a no-no place

    @rad131304 said:

    My watchlist in IE is telling me that save is an empty string.

    Right before the xmlhttp.open, I added
    alert(save);
    alert(save.length);

    they show the XML and the length (1162 bytes, as it happens) in Chrome and IE11.


  • Discourse touched me in a no-no place

    @rad131304 said:

    EDIT: OOOOOOOOH You're reusing the XHR. It must be overwriting the variable waiting for the new response when you call open.

    note how I do it, though. In one button handler, I ask the server for the XML I'm going to use, so I don't have to embed it in this page. I save the XML in the iframe for later, if I get soem back. Then the second button's click handler stuffs it into a POST. I probably don't have to do it that way but it seemed, for my test purposes, the easiest thing to do. I could just, at least for the test, embed it in the page.



  • @FrostCat said:

    note how I do it, though. In one button handler, I ask the server for the XML I'm going to use, so I don't have to embed it in this page. I save the XML in the iframe for later, if I get soem back. Then the second button's click handler stuffs it into a POST. I probably don't have to do it that way but it seemed, for my test purposes, the easiest thing to do. I could just, at least for the test, embed it in the page.

    See my second edit.

    When you open the xhr, you're causing a readystatechange, which causes the handler to execute, overwriting save since you write to it all the time, not just on readystate = 4.


  • Discourse touched me in a no-no place

    @rad131304 said:

    Yes that's definitely it, try this:

    Well, that's an improvement. Now I get a 422 with a Java stack trace and "Unable to determine scheme (HTTP, HTTPS)." It's not the result I wanted, heh, but at least it's not "?SYNTAX ERROR". :smile:



  • @FrostCat said:

    Well, that's an improvement. Now I get a 422 with a Java stack trace and "Unable to determine scheme (HTTP, HTTPS)." It's not the result I wanted, heh, but at least it's not "?SYNTAX ERROR". :smile:

    sorry, I changed your ajax target, so that might have fubared things


  • Discourse touched me in a no-no place

    @rad131304 said:

    When you open the xhr, you're causing a readystatechange, which causes the handler to execute, overwriting save since you write to it all the time, not just on readystate = 4.

    Yup, that was a dumb error. I had intended the save to only happen on readystate = 4 but forgot to finish writing that block, I guess.


  • Discourse touched me in a no-no place

    @rad131304 said:

    sorry, I changed your ajax target, so that might have fubared things

    Naw, I saw that when I got a 404, and fixed it. :smile: I'm guessing the server doesn't like the fact that I only POSTED a relative URL, so I'll try a full one next.



  • @FrostCat said:

    Yup, that was a dumb error. I had intended the save to only happen on readystate = 4 but forgot to finish writing that block, I guess.

    Happens to all of us; I've definitely been there!



  • @FrostCat said:

    Naw, I saw that when I got a 404, and fixed it. :smile: I'm guessing the server doesn't like the fact that I only POSTED a relative URL, so I'll try a full one next.

    How would the server know? It's probably more annoyed that you're doing HTTP over 8080

    Edit: nope that's the proper alternate


  • Discourse touched me in a no-no place

    @rad131304 said:

    How would the server know? It's probably more annoyed that you're doing HTTP over 8080

    Well, it was just a WAG, and it didn't work anyway, heh.

    Tomcat defaults to running on 8080. I could put it on 80 but I would have to read the documentation. The error I got isn't immediately helpful. (I mean, it should have inferred (in this case) http from context.)


Log in to reply
 

Looks like your connection to What the Daily WTF? was lost, please wait while we try to reconnect.