I am trying to understand CORS configuration with vert.x. I found an example at this github repository under the CORS section. When i tried it only POST example seemed to work (preflight.html). Since I also need to use GET (nopreflight.html) in one of my projects, I have tried to modify the example obtaining poor results. This is what i have right now:
Server.java
package io.vertx.example.web.cors;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.MultiMap;
import io.vertx.core.http.HttpMethod;
import io.vertx.example.util.Runner;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.CorsHandler;
import io.vertx.ext.web.handler.StaticHandler;
import java.util.Map;
/*
* @author <a href="mailto:pmlopes@gmail.com">Paulo Lopes</a>
*/
public class Server extends AbstractVerticle {
// Convenience method so you can run it in your IDE
public static void main(String[] args) {
Runner.runExample(Server.class);
}
@Override
public void start() throws Exception {
Router router = Router.router(vertx);
router.route().handler(CorsHandler.create("*")
.allowedMethod(HttpMethod.GET)
.allowedMethod(HttpMethod.POST)
.allowedMethod(HttpMethod.OPTIONS)
.allowedHeader("Access-Control-Request-Method")
.allowedHeader("Access-Control-Allow-Credentials")
.allowedHeader("Access-Control-Allow-Origin")
.allowedHeader("Access-Control-Allow-Headers")
.allowedHeader("X-PINGARUNER")
.allowedHeader("Content-Type"));
router.get("/access-control-with-get").handler(ctx -> {
ctx.response().setChunked(true);
MultiMap headers = ctx.request().headers();
/*for (String key : headers.names()) {
ctx.response().write(key);
ctx.response().write(headers.get(key));
ctx.response().write("\n");
}*/
ctx.response().write("response ");
ctx.response().end();
});
router.post("/access-control-with-post-preflight").handler(ctx -> { ctx.response().setChunked(true);
MultiMap headers = ctx.request().headers();
/*for (String key : headers.names()) {
ctx.response().write(key);
ctx.response().write(headers.get(key));
ctx.response().write("\n");
}*/
ctx.response().write("response ");
ctx.response().end();
});
// Serve the static resources
router.route().handler(StaticHandler.create());
vertx.createHttpServer()
.requestHandler(router::accept)
.listen(8080);
}
}
nopreflight.html
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Simple use of Cross-Site XMLHttpRequest (Using Access Control)</title>
<script type="text/javascript">
//<![CDATA[
var invocation = new XMLHttpRequest();
var url = 'http://localhost:8080/access-control-with-get/';
var invocationHistoryText;
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
function callOtherDomain(){
if(invocation)
{
invocation.open('GET', url, true);
//invocation.setRequestHeader('X-PINGARUNER', 'pingpong');
invocation.setRequestHeader('Content-Type', 'application/xml');
invocation.onreadystatechange = handler;
invocation.send();
}
else
{
invocationHistoryText = "No Invocation TookPlace At All";
var textNode = document.createTextNode(invocationHistoryText);
var textDiv = document.getElementById("textDiv");
textDiv.appendChild(textNode);
}
}
function handler()
{
if (invocation.readyState == 4)
{
if (invocation.status == 200)
{
var response = invocation.responseXML;
//var invocationHistory = response.getElementsByTagName('invocationHistory').item(0).firstChild.data;
invocationHistoryText = document.createTextNode(response);
var textDiv = document.getElementById("textDiv");
textDiv.appendChild(invocationHistoryText);
}
else
alert("Invocation Errors Occured " + invocation.readyState + " and the status is " + invocation.status);
}
else
console.log("currently the application is at " + invocation.readyState);
}
//]]>
</script>
</head>
<body>
<form id="controlsToInvoke" action="">
<p>
<input type="button" value="Click to Invoke Another Site" onclick="callOtherDomain()" />
</p>
</form>
<p id="intro">
This page basically makes invocations to another domain using cross-site XMLHttpRequest mitigated by Access Control. This is the simple scenario that is <em>NOT</em> preflighted, and the invocation to a resource on another domain takes place using a simple HTTP GET.
</p>
<div id="textDiv">
This XHTML document invokes another resource using cross-site XHR.
</div>
</body>
</html>
preflight.html
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Simple use of Cross-Site XMLHttpRequest (Using Access Control)</title>
<script type="text/javascript">
//<![CDATA[
var invocation = new XMLHttpRequest();
var url = 'http://localhost:8080/access-control-with-post-preflight/';
var invocationHistoryText;
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
function callOtherDomain(){
if(invocation)
{
invocation.open('POST', url, true);
invocation.setRequestHeader('X-PINGARUNER', 'pingpong');
invocation.setRequestHeader('Content-Type', 'application/xml');
invocation.onreadystatechange = handler;
invocation.send();
}
else
{
invocationHistoryText = "No Invocation TookPlace At All";
var textNode = document.createTextNode(invocationHistoryText);
var textDiv = document.getElementById("textDiv");
textDiv.appendChild(textNode);
}
}
function handler()
{
if (invocation.readyState == 4)
{
if (invocation.status == 200)
{
var response = invocation.responseText;
//var invocationHistory = response.getElementsByTagName('invocationHistory').item(0).firstChild.data;
invocationHistoryText = document.createTextNode(response);
var textDiv = document.getElementById("textDiv");
textDiv.appendChild(invocationHistoryText);
}
else
{
alert("Invocation Errors Occured " + invocation.readyState + " and the status is " + invocation.status);
}
}
else
{
console.log("currently the application is at " + invocation.readyState);
}
}
//]]>
</script>
</head>
<body>
<form id="controlsToInvoke" action="">
<p>
<input type="button" value="Click to Invoke Another Site" onclick="callOtherDomain()" />
</p>
</form>
<p id="intro">
This page POSTs XML data to another domain using cross-site XMLHttpRequest mitigated by Access Control. This is the preflight scenario and the invocation to a resource on another domain takes place using first an OPTIONS request, then an actual POST request.
</p>
<div id="textDiv">
This XHTML document POSTs to another resource using cross-site XHR. If you get a response back, the content of that response should reflect what you POSTed.
</div>
</body>
</html>
EDIT: Thanks to a suggestion I modified Server.java code to make it clearer and I understood that the problem was the hanlder function in nopreflight.html file. I solved the problem as follows:
EDITED Server.java
public class Server extends AbstractVerticle {
// Convenience method so you can run it in your IDE
public static void main(String[] args) {
Runner.runExample(Server.class);
}
@Override
public void start() throws Exception {
Router router = Router.router(vertx);
Set<String> allowedHeaders = new HashSet<>();
allowedHeaders.add("x-requested-with");
allowedHeaders.add("Access-Control-Allow-Origin");
allowedHeaders.add("origin");
allowedHeaders.add("Content-Type");
allowedHeaders.add("accept");
allowedHeaders.add("X-PINGARUNER");
Set<HttpMethod> allowedMethods = new HashSet<>();
allowedMethods.add(HttpMethod.GET);
allowedMethods.add(HttpMethod.POST);
allowedMethods.add(HttpMethod.DELETE);
allowedMethods.add(HttpMethod.PATCH);
allowedMethods.add(HttpMethod.OPTIONS);
allowedMethods.add(HttpMethod.PUT);
router.route().handler(CorsHandler.create("*")
.allowedHeaders(allowedHeaders)
.allowedMethods(allowedMethods));
router.get("/access-control-with-get").handler(ctx -> {
HttpServerResponse httpServerResponse = ctx.response();
httpServerResponse.putHeader("content-type", "text/html").end("<h1>Success</h1>");
});
EDITED nopreflight.html
<script type="text/javascript">
var xhttp = new XMLHttpRequest();
var url = 'http://localhost:8080/access-control-with-get/';
var invocationHistoryText;
function callOtherDomain() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("textDiv").appendChild(document.createTextNode(xhttp.responseText));
}
};
xhttp.open("GET", url, true);
xhttp.send();
}
</script>
This seems to be working fine for us
after this if you create httpserver, it should work fine.