3

I got this error when I try to POST a JSON file to my server.

On my server-side, the code is:

@POST
@Path("updatedata")
@Produces("text/plain")
@Consumes("application/json")
public Response UpdateData(String info) {
    Gson gson = new Gson();
    List<Data> list = gson.fromJson(info, new TypeToken<List<Data>>() {
    }.getType());

    int is_success = 0;
    try {
      is_success += trainingdata.updateData(list);
    } catch (SQLException e) {
      e.printStackTrace();
    }
    String returnjson = "{\"raw\":\"" + list.size() + "\",\"success\":\"" + is_success + "\"}";
    return Response.ok().entity(returnjson).header("Access-Control-Allow-Origin", "*").header("Access-Control-Allow-Methods", "POST").build();
}

I can update my data successfully through RESTClient - a Chrome Plugin.

But when I build the frontend and try to call the API through javascript, Firefox shows: Cross-Origin Request Blocked: The Same Origin Policy... Chrome shows: XMLHttpRequest cannot load ... No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '...' is therefore not allowed access

I wrote the javascript like this:

var json = JSON.stringify(array);

var xhr = new XMLHttpRequest();
xhr.open("POST", "http://myurl:4080/updatedata", true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(json);

xhr.onload = function (e) {
    if (xhr.readyState === 4) {
        if (xhr.status === 200) {
            alert('hello');
        }
    }
};
xhr.onerror = function (e) {
    console.error(xhr.statusText);
};

Is there any problem with my javascript code?

I deploy my backend code and front-end code on the same machine.

The GET function works successfully.

@GET
@Produces("application/json")
@Path("/{cat_id}")
public Response getAllDataById(@PathParam("cat_id") String cat_id) {
    ReviewedFormat result = null;
    try {
        result = trainingdata.getAllDataById(cat_id);
        Gson gson = new Gson();
        Type dataListType = new TypeToken<ReviewedFormat>() {
        }.getType();
        String jsonString = gson.toJson(result, dataListType);
        return Response.ok().entity(jsonString).header("Access-Control-Allow-Origin", "*").header("Access-Control-Allow-Methods", "GET").build();

    } catch (SQLException e) {
        logger.warn(e.getMessage());
    }
    return null;
}

Front end:

var xhr = new XMLHttpRequest();
xhr.open("GET", "http://URL:4080/mywebservice/v1/trainingdata/" + cat_id, true);

xhr.onload = function (e) {
    if (xhr.readyState === 4) {
        if (xhr.status === 200) {
            //console.log(xhr.responseText);
            var jsoninfo = xhr.responseText;
            var obj = JSON.parse(jsoninfo);
        }
     }
}
xxx
  • 1,153
  • 1
  • 11
  • 23
Freya Ren
  • 2,086
  • 6
  • 29
  • 39

5 Answers5

7

CORS prevents issues from occurring with cross-site attacks and forces smart development by not relying on other people's resources (which could die). Its a default security feature on most servers and browsers.

In Apache you can disable CORS by adding a header, IIS and AppEngine work similarly.

Since you are developing locally, your best bet is either XAMPP/WAMPP plus an appropriate header - or simply switch to FireFox.

FireFox does not consider local files under CORS, while most browsers do.

Apache Fix:

Add header ->

Header set Access-Control-Allow-Origin "*"

Reset Server ->

apachectl -t
  • sudo service apache2 reload

IIS Fix:

Modify web.config in root directory (similar to HTAccess)

<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <system.webServer>
   <httpProtocol>
     <customHeaders>
       <add name="Access-Control-Allow-Origin" value="*" />
     </customHeaders>
   </httpProtocol>
 </system.webServer>
</configuration>

App Engine:

Header Method for Python: self.response.headers.add_header()

class CORSEnabledHandler(webapp.RequestHandler):
  def get(self):
    self.response.headers.add_header("Access-Control-Allow-Origin", "*")
    self.response.headers['Content-Type'] = 'text/csv'
    self.response.out.write(self.dump_csv())

For Java: resp.addHeader()

public void doGet(HttpServletRequest req, HttpServletResponse resp) {
  resp.addHeader("Access-Control-Allow-Origin", "*");
  resp.addHeader("Content-Type", "text/csv");
  resp.getWriter().append(csvString);
}

For Go: w.Header().Add()

func doGet(w http.ResponseWriter, r *http.Request) {
  w.Header().Add("Access-Control-Allow-Origin", "*")
  w.Header().Add("Content-Type", "text/csv")
  fmt.Fprintf(w, csvData)
}

CORS issues can be bypassed via JSONP for GET requests if this interested you: http://en.wikipedia.org/wiki/JSONP

Andrew
  • 995
  • 6
  • 10
  • 1
    *Most of the Time* you can only use JSONP with GET requests. But you are basically correct. – Andrew Nov 12 '14 at 01:50
5

This is an issue caused by making a cross domain request in javascript. The browser prevents this for security reasons.

In javascript you can't make requests to a different domain (including different port) by default.

Your options are to enable CORS or use a reverse proxy if you need to send requests to another domain.

TGH
  • 38,769
  • 12
  • 102
  • 135
  • I put my server code and front end code in the same machine. Should it be a problem? – Freya Ren Nov 12 '14 at 01:38
  • It's not enough if they are hosted on different domains (including different ports) – TGH Nov 12 '14 at 01:38
  • When you mentioned reverse proxy. Do you mean for example, I create a PHP file to call my server. And the front end call this PHP file? – Freya Ren Nov 12 '14 at 01:51
  • Yes that is correct. Make a page on your own domain that in turn calls the other domain on the server. – TGH Nov 12 '14 at 01:52
  • He just needs to enable CORS. Its not rocket science. Headers are the easiest way to go about it in Apache. Local machine just switch to FireFox for dev. I'm working on a writeup on how to do this should be below. – Andrew Nov 12 '14 at 02:04
  • CORS is good, but one thing to keep in mind is that it doesn't work in IE 9 and below. – TGH Nov 12 '14 at 02:05
  • Actually, my GET function can work successfully without other settings. If I change the backend function a little bit, and change the xhr.open('GET', url), I can get data in the browser. How comes? – Freya Ren Nov 12 '14 at 02:10
0

It sounds like you have control over the remote resource you're posting to. If that's the case, your remote resource needs to have the following header:

Access-Control-Allow-Origin: http://yourrequestingurl.com

More information here (It looks like someone asked a question like yours already): Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at

Community
  • 1
  • 1
squaretastic
  • 123
  • 1
  • 11
0

Thanks for @TGH's hint, I finally solve the problem by adding a web proxy.

Refer to Using a Web Proxy, I create a proxy.php file, which receives Javascript's xmlHttpRequest, gets the postdata and calls the web service API.

<?php

$method = isset($_POST['method']) ? $_POST['method'] : 'POST';
$postData = file_get_contents('php://input');
$url = $envArray['url'] . ':' . $envArray['port'] . '/mywebservice/v1/trainingdata/updatedata';

echo curl($url, $postData, $method);
}

function curl($url = '', $params = '', $method = 'POST'){
    if (empty($url)) {
        error_log('Curl URL is empty.');
        return;
    }
    $envArray = Settings::getEnvAry();

    $ch = curl_init(); 
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);                                                                     
    curl_setopt($ch, CURLOPT_POSTFIELDS, html_entity_decode($params, ENT_QUOTES));
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(                                                                          
            'Content-Type: application/json',                                                                                
            'Content-Length: ' . strlen($params)
        )                                                                       
    );
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);                                                                      

    $response = curl_exec($ch);
    $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $rs = array('status' => $http_status, 'response' => $response);

    return json_encode($rs);
}
?>

And in the front end, I call the proxy.php

var xhr = new XMLHttpRequest();
xhr.open("POST", "proxy.php", true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(json);

I think this is better for deploying project onto remote box, instead of modifying Apache configuration.

xxx
  • 1,153
  • 1
  • 11
  • 23
Freya Ren
  • 2,086
  • 6
  • 29
  • 39
0

You can add header in your Nginx configuration and similarly in other web servers

example

add_header Access-Control-Allow-Origin *;