Showing posts with label HTTP. Show all posts
Showing posts with label HTTP. Show all posts

Wednesday, July 11, 2018

Perform an Auto-Submit Form Post from an Angular page

Keywords:
angular angular2 form post auto submit

Problem:
You need to send the user to another application (in the current view or new window) via a form-post that includes a payload of inputs.

How do you:
  1. avoid the form being handled by the angular framework; and (for bonus points)
  2. make the form dynamically auto-submit (to avoid the user having to hit a submit button)?

Solution:
For (a) the key to ensuring the form element in the template is not handled by the angular framework - and therefore allowing it to submit to the defined action is adding the ngNoForm attribute. There's notes in a discussion How to submit form to server in Angular2 ... at the time of writing, it doesn't appear to be well documented.

<form ngNoForm 
    #myFormPost name="myFormPost" 
    action="https://httpbin.org/post" 
    method="POST"
    target="_blank" >
    ...
  </form>

For (b) the dynamic auto-submit, you can obtain the reference to the form and submit it from the component code:
@ViewChild('myFormPost') myFormPost: ElementRef;
  ...
  // in some place where the form has been set
  this.myFormPost.nativeElement.submit();

A full working example is here: angular-form-post-auto-submit (on StackBlitz)
(this includes demo handling of hidden multi-line inputs)


Tuesday, June 04, 2013

Stop Chrome from Prerending URLs

Keywords:
Google Chrome prefetch POST prerender random timeout speculative sockets preconnections browser learning predict network actions

Problem:
Chrome has become my browser of choice - it seems faster and is less reliant on the disk which is critical if you're on machine that's continually doing disk-intensive work (such as builds).

That speed can come at a cost. Perhaps it was an update or perhaps because of the repetition of certain actions in a web-wizard style application in development but all of a sudden using Chrome was giving random unexpected results that weren't reproducible in other browsers.

Tracing the server-side, it seems that URLs are being called - with GET requests - independently of my actions in the browser. These phantom requests include my credentials (HTTP BASIC auth) and session information (as cookies), but on the client-side (tracking Network in Developer Mode) there's no explanation for how/why/when these URLs are being hit.

Is my browser possessed?

Solution:
Short answer? YES, yes it is (kind of) possessed! What I'm seeing is Chrome predicting my next action in the web-application and hitting those URLs (on my behalf) in the interests of speed. It was initially hard to find good references for this because it seems to be referenced with various terms. Officially, it's "Prerender" and the Google Whitepaper "Web Developer's Guide to Prerendering in Chrome" describes it as:

Prerendering extends the concept of prefetching. Instead of just downloading the top-level resource, it does all of the work necessary to show the page to the user—without actually showing it until the user clicks. Prerendering behaves similarly to if a user middle-clicked on a link on a page (opening it in a background tab) and then later switched to that tab. However, in prerendering, that "background tab" is totally hidden from the user, and when the user clicks, its contents are seamlessly swapped into the same tab the user was viewing. From the user’s perspective, the page simply loads much faster than before.

As mentioned in the quote, it's separate to "Prefetching" - which is the mechanism where a page can explicitly flag a URL as safe/recommended to begin fetching.

If this was a chess program, it's like the browser is thinking a few moves ahead but instead of just anticipating moves, it's actually making them.

As raised (and debated) in Chromium Issue 85229 - this time under the term "speculative sockets" - there's no way for the server/application to opt-out or ask it not to do this.

You can view diagnostics for what URLs are being Prerendered by opening the following in a tab (if you're experiencing erroneous/random behaviour it can make for interesting reading):
chrome://net-internals/#prerender

Shouldn't GET requests be safe? As discussed in On Chrome and URL Pre-fetching, yes but surely not always 100% safe. If you have an app that has URLs that only ever get POST requests there's nothing stopping Chrome from hitting these with Prerender GETs - and if the URLs (for convenience) handle the GET requests you could be trouble. Which leads to some inconsistencies in the above mentioned whitepaper for "Situations in which prerendering is aborted" ("...Chrome may run into a situation that could potentially lead to user-visible behavior that is incorrect"):
  • POST, PUT, and DELETE XMLHTTPRequests
    False: although in your usage of an application/site a URL may only ever be invoked via POST, you can't stop it attempting the same URL with a GET, just minus the parameters.
  • Developer Tools is open
    False: In my usage anyway, using the Developer tools (trying to track Network requests) will not stop the Prerender requests being made.

Which (finally) leads to the solution - turn off Prerending. The whitepaper has a section titled "Ensuring you have prerender turned on" (despite it being enabled by default). So disabling it involves following the reverse:
  1. Click the 'three bars' icon in the top right of Chrome.
  2. Click 'Settings'
  3. Click 'Show advanced settings...'
  4. Under 'Privacy'
    • un-check the option 'Predict network actions to improve page load performance'
    • un-check 'Use a prediction service to help complete searches and URLs typed in the address bar'
      (this specifically relates to prefetch/prerender of URLs typed into the 'Omnibox' address bar - Chrome will actually be calling up the suggested sites in the background if they happen to be URLs you commonly select)
  5. Open a new tab and paste in "chrome://net-internals/#prerender"
    Confirm that it lists:
    • Prerender Enabled: false
    • Prerender Omnibox Enabled: false


Notes:
There is some confusion about the requests being made for Prerender and reports of Chrome making multiple (and unnecessary) background requests for the favicon - e.g. Issue 39402. At the server-side it will be clear that Prerender requests are for URLs that you tend to access, before you actually access them rather than URLs ending in /favicon.ico. There doesn't seem to be a way to disable favicon 'polling' but the above mentioned issue seems to indicate improvements were being considered to reduce the server load.


Wednesday, June 18, 2008

HTTP Status 405 - HTTP method POST is not supported by this URL

Keywords:
HTTP Status 405 - HTTP method POST is not supported by this URL form login tomcat

Problem:
What does this error mean when reported by tomcat?
HTTP Status 405 - HTTP method POST is not supported by this URL
Status report

HTTP method POST is not supported by this URL

The specified HTTP method is not allowed for the requested resource (HTTP method POST is not supported by this URL).


Solution:
Simple answer: your servlet needs to implement doPost(HttpServletRequest, HttpServletResponse). Your servlet probably already implements doGet() so if you don't care about implementing different logic for POST, then just get it to call doGet():
public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
        // same logic
        this.doGet(request, response);
}


Notes:
It's a bit confusing if the servlet is a login form. GET should be returning the login form, POST from this form is handled by j_security_check. But if the client's initial request was a POST then the login servlet just needs to implement doPost() to keep the servlet container happy (calling doGet() as mentioned above is fine).

It seems that the servlet container (tomcat at least) makes sure the data from the original POST is not lost once the user gets past the j_security_check: Tomcat - User - [is] post data lost when redirecting

Tuesday, May 13, 2008

get/decode/unescape parameters from URL using Javascript

Keywords:
GET decode unescape request parameter param URL javascript

Problem:
You need to get at the (GET) parameters in the URL of the window with some client-slide javascript. There's no built in mechanism to access these by name in an unencoded way so it would seem you have to parse the window location ... this must have been done by some one before.

Solution:
Found two good blogs that discuss this:

  1. Get URL Parameters Using Javascript - uses regular expressions

  2. Get parameters from URL with JavaScript - uses substrings from the location.search (ie the query portion of the URL)


I found the second approach easier to read, but both miss unescaping the parameter value once obtained ... there's a built in function for that. So here's what I've used:
<script type="text/javascript">
/*
 * Retrieves the query parameter with the given parameter name or empty 
 * string if not defined
 * 
 * @param parameter name of the parameter to retrieve
 */
function queryParam(parameter) {
    var searchString = top.location.search;
    searchString = searchString.substring(1);// ommit the leading '?'

    var paramValue = '';
    var params = searchString.split('&');
    for (i=0; i<params.length;i++) {
        var paramPair = params[i];
        var eqlIndex = paramPair.indexOf('=');
        var paramName = paramPair.substring(0,eqlIndex);
        
        if (paramName == parameter) {
            paramValue = unescape(paramPair.substring(eqlIndex+1));
            return paramValue;
        }
    }
    return paramValue;
}
</script>


So from a URL http://www.example.com/formprocess?name=Fred%20Bloggs&date=01%2f01%2f2008
You can get the unescaped parameter values via:
var name = queryParam('name'); // 'Fred Bloggs'
var date = queryParam('date'); // '01/01/2008' 


Notes:
This doesn't handle multi-valued parameters ... the first (regex) blog post has some comments on how to handle this.

Thursday, August 16, 2007

Firefox does not reflect input form field values via innerHTML

Keywords:
firefox innerHTML form input field value text select textarea

Problem:
In IE (and therefore the GWT test shell running on windows) you can call innerHTML on a DOM object and where it contains HTTP form input fields you will see an up-to-date reflection of the inputs from the user.

On firefox you simply get a reflection of the DOM as it was originally served up to the user.

This is a problem if you want to move HTML around and not loose the inputs already made by the user.

Eg: On clicking the button in firefox you won't see the input entered in the text box.
<form action="" method="get">       
    <SPAN id="MyContent">           
        <input type="text" name="textField" value="" /><br/>
    </SPAN>
</form>           
  
<button onClick="window.alert(MyContent.innerHTML);">discover user input</button>


Solution:
Found the solution on comp.lang.javascript - Firefox does not reflect selected option via innerHTML. I've extended the example code from this post to handle checkbox, radio and textarea ...

The idea is, every input field on the page must have an onBlur="updateDOM(this)" event handler, forcing the DOM to be updated and reflect the user's input.
<script type="text/javascript">
//
// Will be called by input fields when in 'update DOM' mode. This will
// make sure that changes to input fields in the form will be captured
// in the DOM - not necessary in IE but is required in Moz, etc as the DOM
// will otherwise reflect the page as it was initially.
//
// inputField : the input field that has just been tabbed out of (onBlur) OR the ID of the input field
function updateDOM(inputField) {
    // if the inputField ID string has been passed in, get the inputField object
    if (typeof inputField == "string") {
        inputField = document.getElementById(inputField);
    }
    
    if (inputField.type == "select-one") {
        for (var i=0; i<inputField.options.length; i++) {
            if (i == inputField.selectedIndex) {    
                inputField.options[i].setAttribute("selected","selected");
            } else {
                inputField.options[i].removeAttribute("selected");
            }
        }
    } else if (inputField.type == "select-multiple") {
        for (var i=0; i<inputField.options.length; i++) {
            if (inputField.options[i].selected) {
                inputField.options[i].setAttribute("selected","selected");
            } else {
                inputField.options[i].removeAttribute("selected");
            }
        }
    } else if (inputField.type == "text") {
        inputField.setAttribute("value",inputField.value);
    } else if (inputField.type == "textarea") {
        var text = inputField.value;
        inputField.innerHTML = text;
        inputField.setAttribute("value", text);
    } else if (inputField.type == "checkbox") {
        if (inputField.checked) {
            inputField.setAttribute("checked","checked");
        } else {
            inputField.removeAttribute("checked");
        }
    } else if (inputField.type == "radio") {
        var radioNames = document.getElementsByName(inputField.name);
        for(var i=0; i < radioNames.length; i++) {
            if (radioNames[i].checked) {
                radioNames[i].setAttribute("checked","checked");
            } else {
                radioNames[i].removeAttribute("checked");
            }
        }
    }
}
</script>

<form action="" method="get">        
    <SPAN id="MyContent">            
        <input type="text" name="textField" value="" onBlur="updateDOM(this)"/><br/>
    </SPAN>
</form>            
    
<button onClick="window.alert(MyContent.innerHTML);">discover user input</button>


Notes:
It gets slightly trickier if you have input fields that don't get filled in by the user - eg a date picker dropdown, which will set the textbox with the date for the user, hence they never click in the box and trigger the 'onBlur'. In this case, you'd put the onBlur event on the date picker button

Eg:
<input type="text" name="dateField" id="dateField" value="" onBlur="updateDOM(this)"/>
<button id="myDatePicker" onClick="... do datepicking stuff ..." onBlur="updateDOM('dateField')">
 ... date picking image ...
</button>


Post updated (Thu, 25 Mar 2010): With thanks to the helpful commentors, the above script incorporates better handling for textArea & radio fields as well as an issue I came across for 'select-multiple' (was missing from the original script). It should work fine with the update via document or form approaches discussed in the comments - as opposed to the onBlur which continues to be good enough for my usage.

Post updated (Fri, 5 Apr 2013): Beware trying to set innerHTML text with newlines - IE will lose them. Work around is set the value attribute afterwards and this will honour the newlines (and is meaningless for other browsers).

Tuesday, January 23, 2007

GWT POST request doesn't include parameters

Keywords:
GWT POST request parameters HTTP HTML RequestBuilder AJAX

Problem:
Following the GWT documentation for making a http-post but it's not clear what the post data should look like if you want it to include form parameters. The newer documentation for com.google.gwt.http.client has a bit more detail but the server side code processing the request (a java servlet) says there's no parameters in the request.

Solution:
It's not in the documentation, but if you want to post form data you must set the "Content-type" header value in the request to "application/x-www-form-urlencoded"

For Example:
StringBuffer postData = new StringBuffer();
// note param pairs are separated by a '&' 
// and each key-value pair is separated by a '='
postData.append(URL.encode("YourParameterName")).append("=").append(URL.encode("YourParameterValue"));
postData.append("&");
postData.append(URL.encode("YourParameterName2")).append("=").append(URL.encode("YourParameterValue2"));

RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, "/yourserver/formprocessor"));
builder.setHeader("Content-type", "application/x-www-form-urlencoded");
try {
  builder.sendRequest(postData.toString(), this /* or other RequestCallback impl*/);
} catch (RequestException e) {
  // handle this
}


Notes:
This sample chapter from the JavaScript™ Phrasebook is useful: Sending a POST Request