我知道你已经在提供程序托管的应用程序的上下文中回答了自己的问题,但是对于像我这样需要从未基于.NET框架的语言访问REST API(且无法将其项目编写为Web应用程序)的开发人员,我想更详细地介绍一下这个主题。最近,我被要求编写一个需要此功能的iPad应用程序,并最终进行了以下反向工程:
第1步 - 认证
不会实际涵盖此内容,因为有很多在线示例展示了更常见的方法。当使用SharePoint Online时,Microsoft.SharePoint.Client
库似乎大多使用基于声明的身份验证,其中通过在https://login.microsoftonline.com/RST2.srf找到的端点请求令牌。
第2步 - 获取请求摘要(愚笨的方法)
如果你感到懒惰,可以始终获取已验证的cookie,对目标Web的主页进行GET请求,并使用类似于以下正则表达式的内容:
/(<input (?:[^>]*?)name="?__REQUESTDIGEST"?(?:[^>]*?)\/>)/i
从响应中抓取HTML。从那里,只需提取摘要的value
属性即可。
第2步 - 获取请求摘要(SOAP方法)
CSOM库当前在获取其API调用所使用的请求摘要时使用SOAP端点。您可以通过向类似于以下内容的$(SPWebUrl)/_vti_bin/sites.asmx
Web服务发出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>
执行成功时,响应体将类似于:
<?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
块中提取请求摘要。
第二步 - 获取请求摘要(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
{}
执行成功后,响应主体将如下所示:
{
"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
属性中提取。
如果您正在使用CSOM,那么您已经内置了处理此问题的功能。(可能是JSOM,除非它使用__REQUESTDIGEST输入)Microsoft.SharePoint.Client.ClientContext
在内部使用SOAP方法来管理其请求摘要,并通过其GetFormDigestDirect
方法公开此功能。
ClientContext clientContext = new ClientContext(webUrl);
FormDigestInfo formDigest = clientContext.GetFormDigestDirect();
string headerValue = formDigest.DigestValue;
DateTime expirationDate = formDigest.Expiration;
使用说明:虽然 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"
}
}
应该会产生成功的响应。如果你检查响应的头部,你会发现类似这样的东西:
HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Type: application/json
...
X-RequestDigest: 0xAABBCC...00,03 Sep 2014 18:09:34 -0000
...
提取
X-RequestDigest
响应头将允许您在随后的调用中使用它。(我猜超时是从您的新响应时间开始+原始摘要请求中的
$(TimeoutSeconds)
,但我还没有确认)
不幸的是,
X-RequestDigest
标头仅由实际需要请求摘要的REST请求返回。对于不需要请求摘要的请求,例如:
$(SPWebUrl)/_api/web/lists/getByTitle('MyList')/items
,您将不会收到该标头。如果您发现自己需要在原始计时器超时后获取新的摘要,则需要向
$(SPWebUrl)/_vti_bin/sites.asmx
Web服务发起另一个请求。
步骤??? - 处理错误
我们请求失败时的几个示例响应:
下面的响应来自向
$(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."
}
}
}
接下来是一个来自于使用过期请求摘要的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."
}
}
}
{}
),但我认为你也可以POST一个空的主体。如果这对你不起作用,请告诉我,我会在我的帖子中编辑更多信息。 - Charles Grunwaldconsole.log(SP.ClientRuntimeContext.$K[SP.ClientContext.get_current().$26_0()].$c_0);
var context = new SP.ClientContext("/sites/yourCollection"); context.executeQueryAsync(function() { console.log(SP.ClientRuntimeContext.$K[context.$26_0()].$c_0); });
- bdimag