如何从提供商托管的应用获取请求摘要值? [英] How to get request digest value from provider hosted app?

查看:60
本文介绍了如何从提供商托管的应用获取请求摘要值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am developing SharePoint 2013 Provider hosted app using javascript REST Api. In order to perform create (POST), or update (MERGE) operations on sharepoint items I need to set the 'X-RequestDigest' header with the request.

在SharePoint托管的应用程序中,我可以使用 http://contoso.sharepoint. com/SharePointHostedApp/_api/contextinfo 服务以检索请求摘要值;但是,在提供程序托管的应用程序中,我很难获得该价值.

When in SharePoint-hosted apps I was able to use the http://contoso.sharepoint.com/SharePointHostedApp/_api/contextinfo service to retrieve the request digest value; however, I am having trouble getting that value when in a provider hosted app.

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

The first difference of provider hosted app is that now we need to make a cross-domain request since we are not running in a sharepoint site, but in a different domain hosted on a different server. To be clear: instead of

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

我假设我们需要使用SP.RequestExecutor来执行跨域请求. 当我构造请求时,它看起来如下所示(我将实际的url更改为伪造的东西,但基本上,我们是在告诉代理使用宿主网络的代理具有目标并获取/_api/contextinfo端点):

I assumed we need to use the SP.RequestExecutor to execute a cross domain request. When I construct the request it looks like the following (I've changed the actual urls to something fake, but basically we're telling the proxy to use the host web has the target and get the /_api/contextinfo endpoint):

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

但是,我收到此错误:Cannot find resource for the request contextinfo.表示该端点不存在.

However, I receive this error: Cannot find resource for the request contextinfo. meaning that the endpoint does not exist.

我确保将POST方法与正确的application/json;odata=verbose标头一起使用,且标头为空.

I made sure to use the POST method with the correct application/json;odata=verbose headers with an empty body.

如何将请求摘要值从/_api/contextinfo服务发送到提供商托管的应用程序?

How do I get the request digest value from the /_api/contextinfo service to the provider hosted app?

根据我的研究结果:

  • 我们不能使用$('#__REQUESTDIGEST').val();因为这不适用于提供商托管的应用.
  • 由于我不在共享点之外运行,因此我们需要使用跨域请求中的某些内容.
  • 我尝试将跨域请求的目标设置为hostWebUrl和appWebUrl,并且都给出相同的错误.
  • We can't use $('#__REQUESTDIGEST').val(); because that is not available to a provider hosted app.
  • We need to use some from of cross-domain request since I'm running outside of sharepoint.
  • I have tried setting the target of the cross-domain request to both the hostWebUrl and the appWebUrl and both give the same error.

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

There must be some way to get this value, otherwise we would only be limited to read operations when using JavaScript. Has anyone else solved this using javascript?

从技术上讲,我可以尝试在服务器上使用CSOM来实现所需的服务,并使用WebAPI或WCF公开这些服务,但是似乎不必这样做.

Technically I could try to implement the needed services using the CSOM on server and exposing them using WebAPI or WCF but it seem unreasonable to have to implement that.

更新:

我继续尝试添加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."我猜测请求摘要值中包含一些引荐标头信息,表明该请求是服务器请求的;但是,将来使用它发出的请求是从浏览器发出的,这种不匹配可能是无效的可接受原因.

I went ahead and tried adding a WebAPI controller which exposes a service that retrieves the request digest value. This actually does retrieve a request digest value; however, when attempting to use this in the header of future calls I receive the error: "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." I'm guessing that the request digest value has some referer header information in it which indicates it was requested by the server; however, the future requests made with it are from the browser, and this mismatch might be an acceptable reason for it be invalid.

关于尝试添加webAPI控制器的更多说明. 我基于此示例创建了代码: http://code.msdn.microsoft. com/SharePoint-2013-Perform-335d925b ,但将其转换为使用更新的HttpClient. 我重载了Page_Load方法,将contextTokenString存储在WebAPI控制器可以访问的变量中,然后在请求contextinfo时对其进行解析/使用.

Few more notes on the attempt at adding the webAPI controller. I based my code off of this example: http://code.msdn.microsoft.com/SharePoint-2013-Perform-335d925b but converted it to use the newer HttpClient. I overloaded the Page_Load method, stored the contextTokenString in a variable that could be accessed by the WebAPI controller then parsed/used it when requesting the contextinfo.

有人知道这是否是对该错误的正确诊断吗?在请求摘要值中是否编码了某些内容,会阻止像我建议的那样对其进行检索?

Does anyone know if this is a correct diagnosis of that error? Is there something encoded in the request digest value that would prevent it from be able to be retrieved like I suggested?

由于我迫切希望找到答案,因此我还在MSDN论坛上打开了一个相关问题:

I have also opened a related question on the MSDN forums since I'm desperate to find an answer: http://social.msdn.microsoft.com/Forums/sharepoint/en-US/f601fddd-3747-4152-b2d1-4e89f0a771c4/question-about-limitation-of-providerhosted-apps-is-it-possible-to-make-rest-calls-with-javascript?forum=sharepointdevelopmentprevious

我很难相信这可能是提供程序托管的应用程序的局限性,但是考虑到我已经做的所有测试,当您要使用JavaScript编写程序时,我开始怀疑提供程序托管的应用程序的可行性.

I find it very hard to believe this could be a limitation of provider hosted applications, but given all testing I've done, I'm starting to doubt the viability of provider-hosted applications when you want to write in javascript.

请求帮助!

推荐答案

我知道您已经在提供商托管的应用程序上下文中回答了自己的问题,但是对于像我这样的开发人员,需要从以下位置访问REST API一种不基于.NET框架的语言(并且谁不能将其项目编写为Web应用程序)我想在此主题上进行更多扩展.最近,我被要求编写一个需要此功能的iPad应用程序,并最终对以下内容进行了反向工程:

I realize you've already answered your own question within the context of a provider-hosted app, but for developers like myself who need to access the REST API from a language not based in the .NET framework, (and who cannot write their project as a web app) I'd like to expand on the subject a bit more. I was tasked with writing an iPad app recently that required this functionality, and ended up reverse-engineering the following:

由于涉及许多示例,在线展示了更多的常用方法. Microsoft.SharePoint.Client库在使用SharePoint Online时似乎大多使用基于声明的身份验证,令牌是通过位于以下位置的终结点请求的: https://login.microsoftonline.com/RST2.srf

Not going to actually cover this, as there are plenty of examples online that demonstrate the more common methods. The Microsoft.SharePoint.Client libraries mostly seem to use claims-based authentication when working with SharePoint Online, with the token being requested through the endpoint found at: https://login.microsoftonline.com/RST2.srf

如果您感到懒惰,可以随时获取经过身份验证的cookie,向目标网站的首页发出GET请求,并使用如下正则表达式:

If you're feeling lazy, you can always take your authenticated cookies, make a GET request to the homepage of the target web, and use a regular expression like:

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

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

to scrape the HTML from the response. From there, it'd just be a matter of extracting the value attribute for your digest.

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

The CSOM libraries currently use a SOAP endpoint when acquiring the request digest it uses for its API calls. You can do the same by making a SOAP request to the $(SPWebUrl)/_vti_bin/sites.asmx web service similar to the following:

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>

成功执行后,响应主体将类似于:

When executed successfully, the response body will look something like:

<?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>

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

At that point, you can just extract your request digest from the DigestValue block.

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

The last approach I'm aware of uses an OData request made to the $(SPWebUrl)/_api/contextinfo endpoint:

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

{}

成功执行后,响应主体将如下所示:

When executed successfully, the response body will look like the following:

{
    "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)"
}

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

The request digest can then be extracted from the FormDigestValue property.

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

If you're using CSOM, you have functionality for dealing with this built-in. (probably JSOM, too, unless it uses the __REQUESTDIGEST input) Microsoft.SharePoint.Client.ClientContext uses the SOAP approach internally to manage its request digest and publicly exposes this functionality through its GetFormDigestDirect method.

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

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

// Digest expiration
DateTime expirationDate = formDigest.Expiration;

使用注意事项:尽管ClientContext维护并重新使用缓存的表单摘要来处理其请求,但此方法无法使您访问该缓存的值.相反,此方法在每次调用时都要求一个全新的表单摘要,因此,您将需要设置自己的缓存机制,以便在多个请求中重复使用未过期的摘要.

Usage Notes: While ClientContext maintains and reuses a cached form digest for its requests, this method does not give you access to that cached value. Instead, this method requests a brand new form digest with each call, so you'll want to setup your own caching mechanism in order to re-use unexpired digests across multiple requests.

如果您使用的是JSOM API,但无权访问__REQUESTDIGEST输入值,则可以使用ClientContext的缓存摘要. com/juntalis/4ed013b5b2b299b36f5f"rel =" noreferrer>以下扩展名. (感谢 bdimag 指出缓存)

If you're using the JSOM API and don't have access to a __REQUESTDIGEST input value, you can access the ClientContext's cached digest with the following extensions. (Thanks to bdimag for pointing out the cache)

假设您在TimeoutSeconds过去之前使用了请求摘要,则发出有效的REST请求,如下所示:

Assuming you use the request digest before the TimeoutSeconds have elapsed, a valid REST request made like the following:

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"
    }
}

应该会导致成功的响应.如果检查该响应的标题,则会发现类似以下内容的

should result in a successful response. If you inspect the headers of that response, you'll find something like:

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
...

提取X-RequestDigest响应标头将使您可以在后续调用中使用它. (我猜测超时从您的新响应开始+原始摘要请求的$(TimeoutSeconds)开始,但我尚未确认)

Extracting the X-RequestDigest response header will allow you to use it in a subsequent call. (I'm guessing that the timeout starts over from the time of your new response + $(TimeoutSeconds) from the original digest request, but I've yet to confirm)

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

Unfortunately, the X-RequestDigest header is only returned by REST requests that actually require a request digest. You will not receive the header for requests where a request digest is unrequired, such as: $(SPWebUrl)/_api/web/lists/getByTitle('MyList')/items. Should you find yourself needing a new digest after the original has timed out, you'll need to make another request to the $(SPWebUrl)/_vti_bin/sites.asmx web service.

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

A few example responses from when our requests fail:

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

The following response comes from a REST request made to the $(SPWebUrl)/_api/contextinfo endpoint. (no authentication cookies specified)

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."
        }
    }
}

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

Next, a response originating from a REST request made with an expired request digest (Note the X-RequestDigest header specified in the response.. Not sure if that's usable, but it's worth a shot):

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."
        }
    }
}

这篇关于如何从提供商托管的应用获取请求摘要值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆