如何从提供商托管的应用程序获取请求摘要值?

Mat*_*ola 20 sharepoint ms-office sharepoint-api sharepoint-2013

我正在使用javascript REST Api开发SharePoint 2013 Provider托管应用程序.为了对sharepoint项执行创建(POST)或更新(MERGE)操作,我需要使用请求设置"X-RequestDigest"标头.

在SharePoint托管的应用程序中,我能够使用http://contoso.sharepoint.com/SharePointHostedApp/_api/contextinfo服务来检索请求摘要值; 但是,在提供商托管的应用程序中,我无法获得该值.

提供商托管应用程序的第一个区别是,现在我们需要发出跨域请求,因为我们不是在sharepoint站点中运行,而是在不同服务器上托管的不同域中运行.要明确:而不是

$.ajax({
    url: appWebUrl + '/_api/contextinfo',
    method: "POST",
    headers: { "Accept": "application/json; odata=verbose" }
})
Run Code Online (Sandbox Code Playgroud)

我假设我们需要使用它SP.RequestExecutor来执行跨域请求.当我构造请求时,它看起来如下(我已经将实际的url更改为假的,但基本上我们告诉代理使用主机web有目标并获取/_api/contextinfo端点):

https://contoso-6f921c6addc19f.sharepoint.com/ProviderHostedApp/_api/SP.AppContextSite(@target)/contextinfo?@target=%27https://contoso.sharepoint.com%27

但是,我收到此错误:Cannot find resource for the request contextinfo.意味着端点不存在.

我确保使用application/json;odata=verbose带有空体的正确标头的POST方法.

如何从/_api/contextinfo服务获取请求摘要值到提供者托管应用程序?

根据我研究的内容:

  • 我们不能用$('#__REQUESTDIGEST').val(); 因为提供商托管的应用程序无法使用它.
  • 我们需要使用跨域请求中的一些,因为我在sharepoint之外运行.
  • 我已经尝试将跨域请求的目标设置为hostWebUrl和appWebUrl,并且两者都给出了相同的错误.

必须有某种方法来获取此值,否则我们将仅限于使用JavaScript时的读取操作.还有其他人用javascript解决了这个问题吗?

从技术上讲,我可以尝试使用服务器上的CSOM实现所需的服务,并使用WebAPI或WCF公开它们,但必须实现它似乎是不合理的.

更新:

我继续尝试添加一个WebAPI控制器,该控制器公开一个检索请求摘要值的服务.这实际上确实检索了请求摘要值; 但是,当尝试在未来调用的标头中使用它时,我收到错误:"The security validation for this page is invalid and might be corrupted. Please use your web browser's Back button to try your operation again." 我猜测请求摘要值中有一些引用标头信息,表明它是服务器请求的; 但是,未来使用它发出的请求来自浏览器,这种不匹配可能是无效的可接受原因.

关于尝试添加webAPI控制器的几点注意事项.我根据这个例子编写了我的代码:http://code.msdn.microsoft.com/SharePoint-2013-Perform-335d925b,但将其转换为使用较新的HttpClient.我重载了Page_Load方法,将contextTokenString存储在一个变量中,该变量可以由WebAPI控制器访问,然后在请求contextinfo时解析/使用它.

有谁知道这是否是对该错误的正确诊断?请求摘要值中是否有某些内容会阻止它像我建议的那样被检索?

我已经在MSDN论坛上打开了一个相关的问题,因为我迫切希望找到答案:http: //social.msdn.microsoft.com/Forums/sharepoint/en-US/f601fddd-3747-4152-b2d1-4e89f0a771c4 /问题-关于-限制-的- providerhosted -应用程序-是-它,可能对做休息通话,与JavaScript的?论坛= sharepointdevelopmentprevious

我发现很难相信这可能是提供商托管应用程序的限制,但是考虑到我已经完成的所有测试,当你想用javascript编写时,我开始怀疑提供者托管应用程序的可行性.

乞求帮助!

Cha*_*ald 28

我意识到你已经在提供商托管的应用程序的环境中回答了你自己的问题,但对于像我这样的开发人员需要从不是基于.NET框架的语言访问REST API(以及谁不能编写他们的项目)作为一个网络应用程序)我想更多地扩展这个主题.我最近负责编写一个需要此功能的iPad应用程序,并最终对以下内容进行逆向工程:

第1步 - 身份验证

不会真正涵盖这一点,因为有大量的在线示例演示了更常见的方法.在Microsoft.SharePoint.Client使用SharePoint Online时,这些库似乎主要使用基于声明的身份验证,并通过以下位置的端点请求令牌:https://login.microsoftonline.com/RST2.srf

第2步 - 获取请求摘要(哑法)

如果您感到懒惰,可以随身携带经过身份验证的Cookie,向目标网站的主页发出GET请求,并使用正则表达式,例如:

/(<input (?:[^>]*?)name="?__REQUESTDIGEST"?(?:[^>]*?)\/>)/i

从响应中删除HTML.从那里开始,只需要提取value摘要的属性即可.

第2步 - 获取请求摘要(SOAP方法)

CSOM库在获取用于其API调用的请求摘要时,当前使用SOAP端点.您可以通过向$(SPWebUrl)/_vti_bin/sites.asmxWeb服务发出类似于以下内容的SOAP请求来执行相同的操作:

POST $(SPWebUrl)/_vti_bin/sites.asmx HTTP/1.1
Content-Type: text/xml
SOAPAction: http://schemas.microsoft.com/sharepoint/soap/GetUpdatedFormDigestInformation
X-RequestForceAuthentication: true
Host: $(SPSiteHostname)
Expect: 100-continue
Accept-Encoding: gzip, deflate
Cookie: $(Authenticated Cookies - Either "FedAuth=...; rtFa=..." or "SPOIDCRL=...")
Content-Length: $(Whatever)

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <GetUpdatedFormDigestInformation xmlns="http://schemas.microsoft.com/sharepoint/soap/" />
    </soap:Body>
</soap:Envelope>
Run Code Online (Sandbox Code Playgroud)

成功执行后,响应正文将如下所示:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
        <GetUpdatedFormDigestInformationResponse xmlns="http://schemas.microsoft.com/sharepoint/soap/">
            <GetUpdatedFormDigestInformationResult>
                <DigestValue>0x1122334455 ... FF,27 Jul 2015 03:06:54 -0000</DigestValue>
                <TimeoutSeconds>1800</TimeoutSeconds>
                <WebFullUrl>$(SPWebUrl)</WebFullUrl>
                <LibraryVersion>16.0.3208.1222</LibraryVersion>
                <SupportedSchemaVersions>14.0.0.0,15.0.0.0</SupportedSchemaVersions>
            </GetUpdatedFormDigestInformationResult>
        </GetUpdatedFormDigestInformationResponse>
    </soap:Body>
</soap:Envelope>
Run Code Online (Sandbox Code Playgroud)

此时,您只需从DigestValue块中提取请求摘要即可.

第2步 - 获取请求摘要(REST方法)

我所知道的最后一种方法是使用对$(SPWebUrl)/_api/contextinfo端点发出的OData请求:

POST $(SPWebUrl)/_api/contextinfo HTTP/1.1
Host: $(SPSiteHostname)
DataServiceVersion: 3.0
Accept: application/json; odata=nometadata
Content-Type: application/json; odata=verbose
Cookie: $(Authenticated Cookies)
Content-Length: 2

{}
Run Code Online (Sandbox Code Playgroud)

成功执行后,响应正文将如下所示:

{
    "FormDigestTimeoutSeconds" : 1800,
    "FormDigestValue" : "0x1122334455 ... FF,27 Jul 2015 03:06:54 -0000",
    "LibraryVersion" : "16.0.4230.1217",
    "SiteFullUrl" : "$(SPSiteUrl)",
    "SupportedSchemaVersions" : ["14.0.0.0", "15.0.0.0"],
    "WebFullUrl" : "$(SPWebUrl)"
}
Run Code Online (Sandbox Code Playgroud)

然后可以从FormDigestValue属性中提取请求摘要.

第2步 - 获取请求摘要(CSOM方法)

如果您使用的是CSOM,则可以使用此功能来处理此内置功能.(可能是JSOM,除非它使用__REQUESTDIGEST输入)Microsoft.SharePoint.Client.ClientContext在内部使用SOAP方法来管理其请求摘要,并通过其GetFormDigestDirect方法公开公开此功能.

ClientContext clientContext = new ClientContext(webUrl);
// ...
FormDigestInfo formDigest = clientContext.GetFormDigestDirect();

// X-RequestDigest header value
string headerValue = formDigest.DigestValue;

// Digest expiration
DateTime expirationDate = formDigest.Expiration;
Run Code Online (Sandbox Code Playgroud)

使用备注:在ClientContext为其请求维护和重用缓存的表单摘要时,此方法不允许您访问该缓存的值.相反,此方法会在每次调用时请求全新的表单摘要,因此您需要设置自己的缓存机制,以便在多个请求中重复使用未过期的摘要.

第2步 - 获取请求摘要(JSOM方法)

如果您使用的是JSOM API并且无法访问__REQUESTDIGEST输入值,则可以使用以下扩展名访问ClientContext缓存摘要.(感谢bdimag指出缓存)

第3步 - 获取新的请求摘要

假设您在TimeoutSeconds过去之前使用了请求摘要,则会生成如下有效的REST请求:

POST $(SPWebUrl)/_api/web/lists/getByTitle('MyList')/getchanges HTTP/1.1
Host: $(SPSiteHostname)
DataServiceVersion: 3.0
Accept: application/json; odata=nometadata
Content-Type: application/json; odata=verbose
X-RequestDigest: $(Request Digest)
Cookie: $(Authenticated Cookies)
Content-Length: 140

{
    "query" : {
        "__metadata" : {
            "type" : "SP.ChangeQuery"
        },
        "Add" : "True",
        "Item" : "True",
        "Update" : "True"
    }
}
Run Code Online (Sandbox Code Playgroud)

应该会得到成功的回应.如果您检查该响应的标题,您会发现如下内容:

HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Type: application/json;odata=fullmetadata;streaming=true;charset=utf-8
...
X-RequestDigest: 0xAABBCC...00,03 Sep 2014 18:09:34 -0000
...
Run Code Online (Sandbox Code Playgroud)

提取X-RequestDigest响应标头将允许您在后续调用中使用它.(我猜测超时从你的新回复时间+ $(TimeoutSeconds)原始摘要请求开始,但我还没有确认)

遗憾的是,X-RequestDigest标头仅由实际需要请求摘要的REST请求返回.您将不会收到请求摘要未被请求的请求的标头,例如:$(SPWebUrl)/_api/web/lists/getByTitle('MyList')/items.如果您在原件超时后发现自己需要新的摘要,则需要向$(SPWebUrl)/_vti_bin/sites.asmxWeb服务发出另一个请求.

步 ??? - 处理错误

我们的请求失败时的一些示例响应:

以下响应来自对$(SPWebUrl)/_api/contextinfo端点的REST请求.(未指定认证cookie)

HTTP/1.1 403 Forbidden
Cache-Control: private, max-age=0
Content-Type: application/json;odata=nometadata;charset=utf-8
...
Server: Microsoft-IIS/8.5
X-SharePointHealthScore: 0
X-Forms_Based_Auth_Required: $(SPRootSiteUrl)/_forms/default.aspx?ReturnUrl=/_layouts/15/error.aspx&Source=%2f_vti_bin%2fclient.svc%2fcontextinfo
X-Forms_Based_Auth_Return_Url: $(SPRootSiteUrl)/_layouts/15/error.aspx
X-MSDAVEXT_Error: 917656; Access+denied.+Before+opening+files+in+this+location%2c+you+must+first+browse+to+the+web+site+and+select+the+option+to+login+automatically.
DATASERVICEVERSION: 3.0
X-AspNet-Version: 4.0.30319
X-IDCRL_AUTH_PARAMS_V1: IDCRL Type="BPOSIDCRL", EndPoint="$(SiteRelativeUrl)/_vti_bin/idcrl.svc/", RootDomain="sharepoint.com", Policy="MBI"
...
Date: Wed, 12 Aug 2015 02:27:35 GMT
Content-Length: 201

{
    "odata.error" : {
        "code" : "-2147024891, System.UnauthorizedAccessException",
        "message" : {
            "lang" : "en-US",
            "value" : "Access denied. You do not have permission to perform this action or access this resource."
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来,响应来自使用过期请求摘要的REST请求的响应(注意X-RequestDigest响应中指定的标头..不确定它是否可用,但值得一试):

HTTP/1.1 403 FORBIDDEN
Cache-Control: private, max-age=0
Content-Type: application/json;odata=fullmetadata;charset=utf-8
...
Server: Microsoft-IIS/8.5
Set-Cookie: rtFa=$(RtfaAuthCookie)
Set-Cookie: FedAuth=$(FedAuth)
X-SharePointHealthScore: 0
X-RequestDigest: 0x19EFFF80617AB2E48B0A9FF0ABA1440B5301E7445F3859177771BF6A39C7E4A74643108D862505A2C99350B0EDB871EF3DDE960BB68060601268818027F04956,12 Aug 2015 02:39:22 -0000
DATASERVICEVERSION: 3.0
X-AspNet-Version: 4.0.30319
...
Date: Wed, 12 Aug 2015 02:39:22 GMT
Content-Length: 253

{
    "odata.error" : {
        "code" : "-2130575251, Microsoft.SharePoint.SPException",
        "message" : {
            "lang" : "en-US",
            "value" : "The security validation for this page is invalid and might be corrupted. Please use your web browser's Back button to try your operation again."
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Gab*_*yer 0

RequestExecutor 实际上会为您处理 RequestDigest。你不必得到它。

如果由于某种原因,您仍然想要获取 RequestDigest 值,请尝试在不更改上下文站点的情况下进行调用。