Lync UCWA - 创建应用程序提供 HTTP 409:冲突错误

Posted

技术标签:

【中文标题】Lync UCWA - 创建应用程序提供 HTTP 409:冲突错误【英文标题】:Lync UCWA - Create application gives a HTTP 409: Conflict error 【发布时间】:2014-10-03 06:20:51 【问题描述】:

过去几天我一直在尝试使用 Microsoft 的 UCWA API(REST API)为我们的 Lync 服务开发应用程序。要使应用程序正常工作:我首先必须使用对某个 URL 的 POST 请求将其提交给 API。首先,我必须通过服务器进行身份验证,我通过向 API 发布用户名和密码凭据来做到这一点。然后我得到一个访问令牌,我可以通过在每个请求的标头中发布令牌来使用它来向 API 发出进一步的请求。我已经能够让访问令牌正常工作,但是当我尝试通过向 https://lyncextws.company.com/ucwa/oauth/v1/applications 发布 HTTP 请求来注册应用程序时:事情将开始出错。

所有这一切都是通过一个使用 iframe 绕过同源策略的 javascript 文件完成的。

这是我的代码目前的样子:

<!DOCTYPE html>
<html lang="no">
    <head>
        <meta charset="UTF-8" />
        <title>PresInfoDisp</title>
    </head>
    <body>
        <iframe src="https://lyncextws.company.com/Autodiscover/XFrame/XFrame.html" id="xFrame" style="display: none;"></iframe>
        <script type="text/javascript" src="jquery.js"></script>
        <script type="text/javascript">

        var access_token;

        var stage = 0;

        // CONNECT AND AUTHENTICATE WITH LYNC UCWA SERVICE
        function connectAndAuthenticate() 

            stage = 1;

            var request =  
              accepts: 'application/json', 
              type: 'POST', 
              url: 'https://lyncextws.company.com/WebTicket/oauthtoken',
              data: 'grant_type=password&username=alexander@domain.company.com&password=somePassword'
            ; 
            document.getElementById('xFrame').contentWindow.postMessage(JSON.stringify(request), 'https://lyncextws.company.com/WebTicket/oauthtoken');
        

        // REQUEST A USER RESOURCE
        function getUserResourceAuthRequest() 

            stage = 0;

            var request =  
              accepts: 'application/json', 
              type: 'GET', 
              url: 'https://lyncextws.company.com/Autodiscover/AutodiscoverService.svc/root/oauth/user?originalDomain=company.com'
            ; 
            document.getElementById('xFrame').contentWindow.postMessage(JSON.stringify(request), 'https://lyncextws.company.com/Autodiscover/AutodiscoverService.svc/root/oauth/user?originalDomain=company.com');

        

        function getUserResource() 

            stage = 2;

            var request =  
              accepts: 'application/json', 
              type: 'GET', 
              url: 'https://lyncextws.company.com/Autodiscover/AutodiscoverService.svc/root/oauth/user?originalDomain=company.com',
              headers: Authorization: "Bearer "+access_token
            ; 
            document.getElementById('xFrame').contentWindow.postMessage(JSON.stringify(request), 'https://lyncextws.company.com/Autodiscover/AutodiscoverService.svc/root/oauth/user?originalDomain=company.com');

        

        // REGISTER APPLICATION RESOURCE
        function registerApplication() 

            stage = 3;

            var request =  
              accepts: 'application/json', 
              type: 'POST', 
              url: 'https://lyncextws.company.com/ucwa/oauth/v1/applications',
              headers: Authorization: "Bearer "+access_token,
              data: 'userAgent': 'InfoDisp1',  'endpointId' : '2d9dc28d-4673-4035-825c-feb64be28e4e', 'culture': 'en-US'
            ; 
            document.getElementById('xFrame').contentWindow.postMessage(JSON.stringify(request), 'https://lyncextws.company.com/ucwa/oauth/v1/applications');

        

        // GRAB A LIST OF CONTACTS
        function listContacts() 

            stage = 4;

            var request =  
              accepts: 'application/json', 
              type: 'GET', 
              url: 'https://lyncextws.company.com/ucwa/oauth/v1/applications',
              headers: Authorization: "Bearer "+access_token
            ; 
            document.getElementById('xFrame').contentWindow.postMessage(JSON.stringify(request), 'https://lyncextws.company.com/ucwa/v1/applications');

        

        this.receiveMessage = function(message)  

            switch(stage) 
                case 1:

                    var beforeReplace = message.data.replace("/\\/g", "");
                    var json = jQuery.parseJSON(beforeReplace);
                    var json2 = jQuery.parseJSON(json.responseText);
                    access_token = json2.access_token;
                    console.log(json2.access_token);
                    console.log(message);

                    getUserResource();

                    break;
                case 0:
                    console.log(message);

                    connectAndAuthenticate();

                    break;

                case 2:

                    var beforeReplace = message.data.replace("/\\/g", "");
                    var json = jQuery.parseJSON(beforeReplace);
                    var json2 = jQuery.parseJSON(json.responseText);
                    console.log(json2._links.applications.href);

                    window.setTimeout(function()registerApplication(), 5000);

                    break;
                case 3:

                    console.log(message);

                    break;
                case 4:



                break;
            


        ; 
        window.addEventListener('message', this.receiveMessage, false);


        $(window).load(function() 
            getUserResourceAuthRequest();
            //console.log(access_token);
        );

        </script>
    </body>
</html>

当我运行此代码时:最后一个 ajax 查询返回错误 409:冲突,它应该返回 201:已创建

这是我的浏览器 (Google Chrome) 输出的内容:

401: Unauthorized 错误应该发生,但 409 冲突不应该发生。所以这是我的问题: 谁能发现为什么我得到这个 409 错误而不是我应该得到的 201?

Microsoft 的示例代码似乎运行良好,但我想避免使用它,因为我需要很长时间才能熟悉它。

如果缺少数据,您需要发现问题:在 cmets 中告诉我,我会提供!

【问题讨论】:

endpointId 总是那个静态字符串吗? @user1680977 是的。如果我从某个地方获得它?它会在哪里? 对于应用程序的 POST,您的请求标头/正文是什么样的?响应头/正文是什么样的? 【参考方案1】:

如果你替换

data: 'userAgent': 'InfoDisp1',  'endpointId' : '2d9dc28d-4673-4035-825c-feb64be28e4e', 'culture': 'en-US' 

用一串该数据代替 I.E.

data: "'userAgent': 'InfoDisp1',  'endpointId' : '2d9dc28d-4673-4035-825c-feb64be28e4e', 'culture': 'en-US'" 

似乎 data 需要一个字符串,在您的示例中,您正在向它传递一个 JSON 对象。这样做会使您的示例对我有用。

【讨论】:

哇...就是这么简单!谢谢! :)【参考方案2】:

问题似乎出在您的静态 endpointId 上。

在他们的原始帮助程序库中,他们有一个名为 generateUUID() 的方法,该方法位于 GeneralHelper 中。最好的想法是使用该方法,但是,如果您想创建自己的方法,那就去做吧。重点是你的每个应用程序必须有不同的endpointId。

【讨论】:

我已经尝试实现和使用该功能。它没有任何区别。还是一样的 409 错误。 嗯...我很确定就是这样。好吧,也许尝试使用辅助库?它们很有帮助 + 您不必自己编写所有内容 如果他使用静态端点,它会将响应代码(假设应用程序仍然有效)从 201(已创建)更改为 200(确定)。此时将返回原始应用程序。【参考方案3】:

您是否只是为了简洁而忽略了自动发现过程,还是真的在代码中跳过了自动发现并假设了发布“创建应用程序”的 URI?

对我来说,第二种情况似乎更多,这是不对的: 需要从用户资源请求的响应中检索创建应用程序的 URI(在您发布的代码中的 getUserResource 中)。 您在那里有一个名为 applications 的链接;它的值包含创建应用程序的正确 URI。

http://ucwa.lync.com/documentation/KeyTasks-CreateApplication

附:我在这里也发布了关于endpointId,看到我无法在上面发表评论 允许在不同的客户端使用同一个应用的endpointId。绝对不能假设在不同客户端上使用相同 endpointId 的应用程序会产生相同的基本应用程序资源 URI

【讨论】:

我尝试使用自动发现并从收到的响应中检索所有 URL。我阅读了有关创建应用程序的 UCWA 文档,并完全按照描述执行了步骤,但它仍然出现 409:冲突。我正在尝试阅读和理解 Microsoft 的示例代码,但对象模型有点过头了。如果我弄清楚了:我会发布答案。我会尽快将代码更新为我当前的代码。 这听起来更奇怪,还因为根据文档 409 并不是真正期望由此操作返回的状态代码。列出的唯一可能的返回码是 403 -> applications【参考方案4】:

我在使用 curl 来试验这个 API 时遇到了同样的问题,并且在同一时间失败了,直到我发现在这种情况下我需要将 content-type 标头设置为 json:

curl -v --data "'userAgent': 'test', 'endpointId': '54321', 'culture':'en-US', 'instanceID':'testinstance'" --header 'Content-Type: application/json' --header 'Authorization: Bearer cwt=AAEBHAEFAAAAAAA..' 'https://lyncserver.com/ucwa/oauth/v1/applications'

成功了!

【讨论】:

以上是关于Lync UCWA - 创建应用程序提供 HTTP 409:冲突错误的主要内容,如果未能解决你的问题,请参考以下文章