Showing posts with label preflight response. Show all posts
Showing posts with label preflight response. Show all posts

Thursday, July 6, 2017

HTTP access control (CORS)


In the case that there is a need to post to a different domain by using JavaScript, you may see CORS issue with error such as:


XMLHttpRequest cannot load https://xyz. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.



XMLHttpRequest cannot load https://xyz. request header field content-type is not allowed by access-control-allow-headers in preflight response.


When you search this issue and many will suggest adding a header:
"Access-Control-Allow-Origin", "*"

However this is not enough, the following is a code from Swagger example that should be able to handle normal situations:


package io.swagger.sample.util;

import java.io.IOException;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;

public class ApiOriginFilter implements javax.servlet.Filter {
  @Override
  public void doFilter(ServletRequest request, ServletResponse response,
      FilterChain chain) throws IOException, ServletException {
    HttpServletResponse res = (HttpServletResponse) response;
    res.addHeader("Access-Control-Allow-Origin", "*");
    res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
    res.addHeader("Access-Control-Allow-Headers", "Content-Type");
    chain.doFilter(request, response);
  }

  @Override
  public void destroy() {
  }

  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
  }
}




Here is another suggestion:


response.addHeader("Access-Control-Allow-Origin", "*");response.addHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT");response.addHeader("Access-Control-Allow-Credentials", "true");response.addHeader("Access-Control-Allow-Headers","Origin, X-Atmosphere-tracking-id, X-Atmosphere-Framework, X-Cache-Date, Content-Type, X-Atmosphere-Transport, *");response.addHeader("Access-Control-Request-Headers","Origin, X-Atmosphere-tracking-id, X-Atmosphere-Framework, X-Cache-Date, Content-Type, X-Atmosphere-Transport, *");


A web page to test RESTFUL JSON post:

<!DOCTYPE html>
<html lang="en">
<head>
<title>RESTFUL API TEST</title>
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/json2/20160511/json2.min.js"></script>
</head>
<body>
<div class="container">
  <h1>RESTFUL SERVICE ENDPOINT</h1>
  <form id="form" enctype='application/json' method="post"
    action="https://hostname:port/myapp/v3/api/endpoint">
    <fieldset>
        <legend>Enter sessionid and username</legend>
        <label>session id</label>
        <input name="sessionid" id="sessionid" type="text">
        <label>username</label>
        <input name="username" id="username" type="text">
        <button class="btn" id="submit" type="submit">submit</button>
    </fieldset>
  </form>
</div>
<div class="container">
  <fieldset>
    <legend>Response</legend>
    <label>status</label><input name="status" id="status" type="text">
    <p>
    <textarea id="responseJson" name="responseJson" rows="8" cols="80"></textarea>
  </fieldset>
</div>
       <script type="text/javascript">
       $("form").submit(function(e) {
            // stop the regular form submission
            e.preventDefault();

             // collect the form data while iterating over the inputs
             var data = {};
             for (var i = 0, ii = form.length; i < ii; ++i) {
                 var input = form[i];
                 if (input.name) {
                     data[input.name] = input.value;
                 }
             }

             // construct an HTTP request
             var xhr = new XMLHttpRequest();
             xhr.open(form.method, form.action, true);
             xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
             xhr.onreadystatechange = function() {
                 $("input[name='status']").val(xhr.status);
                 if (xhr.readyState === 4
                         && (xhr.status === 201
                                 || xhr.status === 400 || xhr.status === 500)) {
                     console.log(xhr.responseText);
                     $("textarea[name='responseJson']").val(
                             xhr.responseText);
                 }
             };

             // send the collected data as JSON
             xhr.send(JSON.stringify(data));
    });
    </script>
</body>
</html>



Sometimes, more constraints may be required to limit the domains that could access the resource:


response.addHeader("Access-Control-Allow-Origin", "myhost.mydomain");


Multiple values are not allowed here. 

What you may do here is, in the servlet filter, checks the original against a list of the domains that are allowed to access the resources, then put the original domain in the  "Access-Control-Allow-Origin" header then return it to the client.