Tuesday, February 27, 2018

Google App Engine / Servlet / Dart client / CORS

The following would be obvious to the informed, but to the newbie trying to write a simple end-end sample, using a servlet on Google's App Engine communicating with a Dart / Angular client, it may be helpful.

You are trying to follow along the HTTP Client example on Dart for the web.
You have written a servlet on app engine, serving the list of heroes as json data.
You point your browser at the app engine URL and you see your json list.
You run your Dart Angular client code, from say WebStorm, but you don't see the list (but other text on the page shows up fine).
You open the browser development console and see an error  :

"XHMLHttpRequest cannot load . No 'Access-Control-Allow-Origin' header is present on the requested resource.  Origin 'http://localhost:42705' is therefore not allowed access."




The HTTP Client tutorial outlines the XMLHttpRequest issue.  In the article Using Dart with JSON Web Services there is the guidance under "A note on CORS and HttpRequest":

"One caveat: Make sure your app is served from the same origin (domain name, port, and application layer protocol) as the web service you are trying to access with HttpRequest. Otherwise your app will hit the Access-Control-Allow-Origin restriction build into your web browser. This is a security restriction to prevent loading data from a different server than the one serving the client app."

This is the situation during development, where the browser page launched by WebStorm is pointed to a local server hosting the page, but that page refers to json data coming from another origin.

The article references code for a Dart server to correctly handle the CORS interaction with the browser, commented nicely:

/**
* Add Cross-site headers to enable accessing this server from pages
* not served by this server
*
* See: http://www.html5rocks.com/en/tutorials/cors/
* and http://enable-cors.org/server.html
*/

So adding the doOptions method to the default servlet template (doPost and doGet) and setting the headers:

@Overrideprotected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
    // pre-flight request processing    resp.setHeader("Access-Control-Allow-Origin", "*");
    resp.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
    resp.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
}
@Overridepublic void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws IOException {

    resp.setHeader("Access-Control-Allow-Origin", "*");
    resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
    resp.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    resp.setContentType("application/json");

    // ... 

The browser should now handshake with app engine correctly, eliminating the browser exception and you should see your json data rendered.

(There is also a topic Google App Engine and CORS on StackOverflow, it notes ...Allow-Headers only as "SUPPORTED_HEADERS" ... whereas the detail above worked for me)