如何通过 Preflight Request 从 javascript 调用 REST Webservice?

Posted

技术标签:

【中文标题】如何通过 Preflight Request 从 javascript 调用 REST Webservice?【英文标题】:How to invoke REST Webservice from the javascript by Preflight Request? 【发布时间】:2013-12-30 21:31:39 【问题描述】:

我正在尝试从 javascript 本身调用位于另一个域中的服务。我可以请求跨域服务。但我无法从服务中检索信息。一些我如何被同源政策阻止。请帮我找出代码中的错误。

我的客户端 Javascript 代码:

var requestJsonData;

function crossDomainCall()  ** It will be called by button click **
    requestJsonData = createCORSRequest('POST', 'IPAddress/servicePath');
    if (requestJsonData)
        requestJsonData.onreadystatechange = handler;
        requestJsonData.send();
    
    else 
        alert('Cross Domain Call is not invoked');
    


function handler(evtXHR) 
    if(requestJsonData.readyState   ==  4) 
        if(requestJsonData.status   ==  200) 
            var response    =   requestJsonData.responseText;
        
        else 
            alert(" Invocation Errors Occured " + requestJsonData.readyState + " and the status is " + requestJsonData.status);
        
    
    else 
        alert("currently the application is at " + requestJsonData.readyState);
    

function createCORSRequest(method, url)
    var xhr;
        xhr = new XMLHttpRequest();
    if ("withCredentials" in xhr)
        xhr.open(method, url, true);
        xhr.setRequestHeader('X-PINGOTHER', 'pingpong');
     else if (typeof XDomainRequest != "undefined")
        xhr = new XDomainRequest();
        xhr.open(method, url);
     else 
        xhr = null;
    
    return xhr;

服务代码:

@OPTIONS
@Path("/servicePath")
@Produces("*/*")
@Consumes("*/*")
public Response corsRequest() 
    Response response   =   null;
    ResponseBuilder builder =   null;
    builder =   Response.ok();
    builder.header("Access-Control-Allow-Headers", "X-PINGOTHER");
    builder.header("Access-Control-Max-Age","1728000");
    builder.header("Access-Control-Allow-Origin","Origin_Ip_Address");
    builder.header("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
    builder.header("Content-Type","text/plain");
    builder.header("Connection", "Keep-Alive");
    response    =   builder.build();
    System.out.println("Exited from Options method");
    return response;


@POST
@Path("/servicePath")
@Produces("application/json")
public String drawRegions() 
    System.out.println("Entered inside Post method");
            // Some calculation to arrive jsonObject.
    return jsonObject;

从代码中,我收到了以下结果。

OPTIONS 方法请求和响应标头

请求标头:

选项 /SolartisGeoCodeLookUpService/Service/drawRegions HTTP/1.1

主机:Cross_Domain_IP_Address

用户代理:Mozilla/5.0 (X11; Linux x86_64; rv:25.0) Gecko/20100101 Firefox/25.0

接受:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8

接受语言:en-US,en;q=0.5

接受编码:gzip、deflate

来源:Origin_IP_Address

访问控制请求方法:POST

访问控制请求标头:x-pingother

连接:保持活动

编译指示:无缓存

缓存控制:无缓存

响应标头

HTTP/1.1 200 正常

服务器:Apache-Coyote/1.1

访问控制允许标头:X-PINGOTHER

连接:保持活动状态

访问控制允许来源:Origin_IP_Address

访问控制最大年龄:1728000

访问控制允许方法:POST、GET、OPTIONS

内容类型:文本/纯文本

内容长度:0

日期:格林威治标准时间 2013 年 12 月 12 日星期四 12:39:27

响应缓存标头

来自缓存的响应标头

Access-Control-Allow-Head... X-PINGOTHER 访问控制允许方法... POST、GET、OPTIONS 访问控制最大年龄 1728000 连接保活 内容长度 0 内容类型 text/plain 日期 2013 年 12 月 12 日星期四 12:39:27 GMT 服务器 Apache-Coyote/1.1 访问控制允许原始 Origin_IP_Address

POST 方法请求和响应标头

请求标头

POST /servicePath HTTP/1.1

主机:crossDomain_IP_Address

用户代理:Mozilla/5.0 (X11; Linux x86_64; rv:25.0) Gecko/20100101 Firefox/25.0

接受:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8

接受语言:en-US,en;q=0.5

接受编码:gzip、deflate

X-PINGOTHER:乒乓球

来源:Origin_IP_Address

连接:保持活动

编译指示:无缓存

缓存控制:无缓存

内容长度:0

响应标头

HTTP/1.1 200 正常

服务器:Apache-Coyote/1.1

内容类型:文本/json

内容长度:128

日期:格林威治标准时间 2013 年 12 月 12 日星期四 12:39:27

附加信息 从 javascript 中调用了两次处理程序方法。第一次,它提出“当前应用程序处于 2” - readyState 值。第二次出现“Invocation Errors Occured 4(readyState value) and status code is 0 (response status code)”。第二次响应明确表示,调用服务已被同源策略停止。但我不知道如何克服这个问题并且必须访问资源。请通过更正我的代码来帮助我。

【问题讨论】:

你能用 Logger.INFO() 代替 System.out 吗?我认为您的 CORS 实施应该有问题。你是否在 web.xml 中包含了你的 CORSFilter 实现包? @sivatumma:我没明白你的意思。如何在 web.xml 中包含 CORSFilter 实现?为什么我们在 web.xml 中需要它? 假设两个域都由您编码,为了允许跨源请求,您的服务必须实现ContainerResponseFilter 及其public ContainerResponse filter(ContainerRequest req, ContainerResponse contResp) ... 方法。您还应该让您的 web.xml 知道这是它应该服务的 servlet。 【参考方案1】:

与其在 javascript 中处理 X 域调用,不如为您的应用程序开发一个本地服务来使用另一个域中的 Web 服务,然后您可以从 javascript 调用您的本地服务。

我还建议您使用 jQuery 来执行跨域 Ajax 调用,请参阅此链接:http://www.pureexample.com/jquery/cross-domain-ajax.html。

没有必要直接处理 XHR,因为你有 jQuery 为你做这件事。

希望对你有帮助,

问候。

【讨论】:

我觉得从本地服务拨打电话可以解决。当我们可以从 javascript 调用其他域时,为什么我们不使用它?如果我遗漏了什么,请清除我的视图。 此技术也称为网络代理,是常见的做法,请查看此链接developer.yahoo.com/javascript/howto-proxy.html。关于 javascript Xdomain 调用,有些域支持这种类型的调用,它可以在他们的 Web 服务器中配置,就像这里所说的 bionicspirit.com/blog/2011/03/24/cross-domain-requests.html。

以上是关于如何通过 Preflight Request 从 javascript 调用 REST Webservice?的主要内容,如果未能解决你的问题,请参考以下文章

Angular 6 - Spring MVC :: Options preflight request throws 500 internal Server Error

CORS Preflight 请求是不是包含请求的标头?

HTTP协议详解(HyperText Transfer Protocol 超文本传输协议)访问控制(CORS) (OPTIONS预请求preflight request)浏览器同源策略

解决Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight 跨域问题

Preflight(options) 请求的正确成功状态代码是啥? [复制]

Preflight 未通过控制检查 Access-Control-Allow-Origin Not Present