我有一个带有一些泽西岛休息服务的Jetty http服务器。这些服务是从在节点服务器上运行的React网站调用的。
由于此设置的跨源性质,我不得不添加一些HTTP标头。基本上,我的所有webservices都返回一个createOkResult()
,其创建如下。
@POST
@Path("orders/quickfilter")
@Consumes(MediaType.APPLICATION_JSON)
public Response getQuickFilterProductionOrders(String data)
{
...
return createOkResult(json.toString());
}
protected Response createOkResult(Object result)
{
return buildCrossOrigin(Response.ok().entity(result));
}
protected static Response buildCrossOrigin(Response.ResponseBuilder responseBuilder)
{
return responseBuilder.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
.allow("OPTIONS")
.build();
}
对于@GET
webservices工作正常。但是当我创建一个@POST
服务时,我无法让它工作。
Web浏览器(chrome和firefox)会返回以下错误:
从'http://localhost:59187/rs/production/orders/quickfilter'访问'http://localhost:3000'的XMLHttpRequest已被CORS策略阻止:请求的资源上没有'Access-Control-Allow-Origin'标头。
所以,乍一看我会想到标题仍然缺失。问题是,当我使用像Postman这样的工具测试此服务时,所有标头都会就位,服务甚至会返回请求的数据。
这是POST请求的屏幕截图。
从我的前端(在节点服务器上运行),我使用axios API,它使用promises,我的请求如下所示:
const url = "http://localhost:59187/rs/production/orders/quickfilter";
const data = JSON.stringify(request);
const headers = { headers: { "Content-Type": "application/json" } };
const promise = axios.post(url, data, headers);
现在我有一个HTTP错误500,如果我删除内容类型标题,我得到一个不受支持的媒体异常。所以,我有理由相信内容类型没问题。
Paul Samsotha向我指出了正确的方向。我最后在ServletContextHandler
上添加了一个过滤器。与链接文章不同,我不必从头开始创建该过滤器。我可以使用现有的过滤器类:即org.eclipse.jetty.servlets.CrossOriginFilter
。
FilterHolder filterHolder = context.addFilter(CrossOriginFilter.class, "/*", EnumSet.allOf(DispatcherType.class));
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,PUT,POST,DELETE,OPTIONS");
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "Content-Type,Authorization,X-Requested-With,Content-Length,Accept,Origin");
filterHolder.setInitParameter(CrossOriginFilter.ALLOW_CREDENTIALS_PARAM, "true");
filterHolder.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, "false");
可能会遗漏上述某些参数,因为它们是默认值。但对我来说似乎至关重要的是将CHAIN_PREFLIGHT_PARAM
设置为false
。
一个很好的副作用是,我可以简化实际服务的代码。他们不再需要添加特殊标题,相比之下他们现在可以返回Response.ok().entity(result).build();
。