library item example consultas sharepoint ms-office sharepoint-2013 sharepoint-api

sharepoint - item - ¿Cómo obtener el valor de resumen de solicitud de la aplicación alojada por el proveedor?



sharepoint rest api filter (4)

Estoy desarrollando la aplicación alojada del proveedor de SharePoint 2013 utilizando javascript REST Api. Para realizar operaciones de creación (POST) o actualización (MERGE) en elementos de puntos compartidos, debo establecer el encabezado ''X-RequestDigest'' con la solicitud.

Cuando estaba en aplicaciones alojadas en SharePoint, pude usar el servicio http://contoso.sharepoint.com/SharePointHostedApp/_api/contextinfo para recuperar el valor de resumen de la solicitud; sin embargo, estoy teniendo problemas para obtener ese valor cuando estoy en una aplicación alojada por un proveedor.

La primera diferencia de la aplicación alojada por el proveedor es que ahora debemos realizar una solicitud de dominio cruzado ya que no estamos ejecutando en un sitio de sharepoint, sino en un dominio diferente alojado en un servidor diferente. Para ser claros: en lugar de

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

Asumí que necesitamos usar el SP.RequestExecutor para ejecutar una solicitud de dominio cruzado. Cuando construyo la solicitud, se parece a lo siguiente (he cambiado las URL reales a algo falso, pero básicamente le estamos diciendo al proxy que use la web del host tiene el objetivo y obtener el punto final /_api/contextinfo ):

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

Sin embargo, recibo este error: Cannot find resource for the request contextinfo. lo que significa que el punto final no existe.

Me aseguré de usar el método POST con la application/json;odata=verbose correcta application/json;odata=verbose headers con un cuerpo vacío.

¿Cómo obtengo el valor de resumen de solicitud del servicio /_api/contextinfo a la aplicación alojada por el proveedor?

Basado en lo que he investigado:

  • No podemos usar $(''#__REQUESTDIGEST'').val() ; porque eso no está disponible para una aplicación alojada por un proveedor.
  • Necesitamos usar alguna solicitud de dominio cruzado ya que estoy ejecutando fuera de sharepoint.
  • He intentado establecer el destino de la solicitud de dominio cruzado tanto para hostWebUrl como para appWebUrl y ambos dan el mismo error.

Debe haber alguna forma de obtener este valor, de lo contrario, solo estaríamos limitados a las operaciones de lectura al utilizar JavaScript. ¿Alguien más ha resuelto esto usando javascript?

Técnicamente, podría intentar implementar los servicios necesarios utilizando el CSOM en el servidor y exponiéndolos utilizando WebAPI o WCF, pero no parece razonable tener que implementar eso.

ACTUALIZAR:

Seguí adelante e intenté agregar un controlador WebAPI que expone un servicio que recupera el valor de resumen de la solicitud. Esto realmente recupera un valor de resumen de solicitud; sin embargo, al intentar usar esto en el encabezado de futuras llamadas, recibo el 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." Supongo que el valor de resumen de la solicitud tiene alguna información de encabezado de referencia que indica que fue solicitado por el servidor; sin embargo, las solicitudes futuras realizadas con él provienen del navegador, y esta falta de coincidencia podría ser una razón aceptable para que no sea válida.

Algunas notas más sobre el intento de agregar el controlador webAPI. Basé mi código en este ejemplo: http://code.msdn.microsoft.com/SharePoint-2013-Perform-335d925b pero lo convertí para usar el HttpClient más nuevo. Sobrecargué el método Page_Load, almacené el contextTokenString en una variable a la que podía acceder el controlador WebAPI, luego lo analicé / usé cuando solicité el contextinfo.

¿Alguien sabe si este es un diagnóstico correcto de ese error? ¿Hay algo codificado en el valor de resumen de la solicitud que impida que se pueda recuperar como sugerí?

También he abierto una pregunta relacionada en los foros de MSDN ya que estoy desesperado por encontrar una respuesta: 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

Me resulta muy difícil creer que esto podría ser una limitación de las aplicaciones hospedadas por el proveedor, pero dadas todas las pruebas que he hecho, estoy empezando a dudar de la viabilidad de las aplicaciones hospedadas por el proveedor cuando desea escribir en javascript.

Pidiendo ayuda!


Debe recordar que en el nivel de permisos existe una comprobación que deshabilita todos los servicios en _api

_api / web / lists _api / search / query? querytext = ''SharePoint'' _api / SP.UserProfiles.PeopleManager

Usted habilita que asegure

configuración del sitio-> permisos del sitio-> nivel de permisos-> lectura->

Características del cliente de integración Usar interfaz remota

Encontré la solución en https://letrasandnumeros.com/2017/02/28/unauthorizedaccessexception-sharepoint-_api/


El RequestExecutor realmente se encarga del RequestDigest por usted. No tienes que conseguirlo.

Si por alguna razón, aún desea obtener el valor de RequestDigest, intente hacer la llamada sin cambiar el sitio de contexto.


Me doy cuenta de que ya ha respondido su propia pregunta en el contexto de una aplicación alojada por un proveedor, pero para los desarrolladores como yo que necesitan acceder a la API REST desde un idioma que no se basa en el marco .NET (y que no pueden escribir su proyecto como una aplicación web) Me gustaría ampliar un poco más el tema. Hace poco me encargué de escribir una aplicación para iPad que requería esta funcionalidad, y terminé realizando una ingeniería inversa de lo siguiente:

Paso 1 - Autenticación

No voy a cubrir esto realmente, ya que hay muchos ejemplos en línea que demuestran los métodos más comunes . La mayoría de las bibliotecas de Microsoft.SharePoint.Client utilizan la autenticación basada en notificaciones cuando trabajan con SharePoint Online, y el token se solicita a través del punto final que se encuentra en: https://login.microsoftonline.com/RST2.srf

Paso 2 - Adquirir el Compendio de Solicitud (Enfoque Mudo)

Si se siente perezoso, siempre puede tomar sus cookies autenticadas, realizar una solicitud GET a la página de inicio de la web de destino y usar una expresión regular como:

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

Para raspar el HTML de la respuesta. A partir de ahí, solo será una cuestión de extraer el atributo de value para su resumen.

Paso 2 - Adquirir el compendio de solicitudes (enfoque SOAP)

Las bibliotecas CSOM actualmente utilizan un punto final SOAP cuando adquieren el resumen de solicitud que utiliza para sus llamadas a la API. Puede hacer lo mismo haciendo una solicitud SOAP al servicio web $(SPWebUrl)/_vti_bin/sites.asmx similar al siguiente:

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>

Cuando se ejecuta con éxito, el cuerpo de la respuesta se verá algo como:

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

En ese momento, simplemente puede extraer su resumen de solicitud del bloque DigestValue .

Paso 2 - Adquirir el compendio de solicitud (enfoque REST)

El último enfoque que conozco utiliza una solicitud OData realizada al punto final $(SPWebUrl)/_api/contextinfo :

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

Cuando se ejecuta con éxito, el cuerpo de la respuesta tendrá el siguiente aspecto:

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

El resumen de la solicitud se puede extraer de la propiedad FormDigestValue .

Paso 2 - Adquirir el Resumen de Solicitud (Enfoque CSOM)

Si está utilizando CSOM, tiene una funcionalidad para tratar con este incorporado. (probablemente JSOM, también, a menos que use la entrada __REQUESTDIGEST) Microsoft.SharePoint.Client.ClientContext usa el enfoque SOAP internamente para administrar su resumen de solicitudes y expone públicamente esta funcionalidad a través de su método GetFormDigestDirect .

ClientContext clientContext = new ClientContext(webUrl); // ... FormDigestInfo formDigest = clientContext.GetFormDigestDirect(); // X-RequestDigest header value string headerValue = formDigest.DigestValue; // Digest expiration DateTime expirationDate = formDigest.Expiration;

Notas de uso : Aunque ClientContext mantiene y reutiliza un resumen de formulario en caché para sus solicitudes, este método no le da acceso a ese valor en caché. En su lugar, este método solicita un resumen de formulario completamente nuevo con cada llamada, por lo que querrá configurar su propio mecanismo de almacenamiento en caché para reutilizar los resúmenes no vencidos en múltiples solicitudes.

Paso 2 - Adquirir el compendio de solicitud (Enfoque JSOM)

Si está utilizando la API de JSOM y no tiene acceso a un valor de entrada __REQUESTDIGEST , puede acceder al ClientContext en caché de ClientContext con las siguientes extensiones . ( Gracias a bdimag por señalar el caché )

Paso 3 - Adquirir nuevos resúmenes de solicitudes

Suponiendo que utilice el resumen de la solicitud antes de que transcurran los TimeoutSeconds , se realizará una solicitud REST válida como la siguiente:

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

debe resultar en una respuesta exitosa. Si inspecciona los encabezados de esa respuesta, encontrará algo como:

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

Extraer el encabezado de respuesta X-RequestDigest le permitirá utilizarlo en una llamada posterior. (Supongo que el tiempo de espera comienza $(TimeoutSeconds) desde el momento de su nueva respuesta + $(TimeoutSeconds) desde la solicitud de resumen original, pero aún tengo que confirmar)

Desafortunadamente, el encabezado X-RequestDigest solo es devuelto por solicitudes REST que realmente requieren un resumen de solicitud. No recibirá el encabezado de las solicitudes en las que no se requiere un resumen de la solicitud, como: $(SPWebUrl)/_api/web/lists/getByTitle(''MyList'')/items . En caso de que necesite un nuevo resumen después de que el tiempo original haya caducado, deberá realizar otra solicitud al servicio web $(SPWebUrl)/_vti_bin/sites.asmx .

Paso ??? - Errores de manejo

Algunos ejemplos de respuestas de cuando nuestras solicitudes fallan:

La siguiente respuesta proviene de una solicitud REST realizada en el punto final $(SPWebUrl)/_api/contextinfo . (no se especifican cookies de autenticación)

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

A continuación, una respuesta que se origina a partir de una solicitud REST realizada con un resumen de solicitud caducado (tenga en cuenta el encabezado X-RequestDigest especificado en la respuesta ... No estoy seguro de si es utilizable, pero vale la pena intentarlo):

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


Ok, hice una nueva aplicación alojada por un proveedor para volver a probar el problema.

Puedes ver el repositorio aquí:

https://github.com/mattmazzola/providerhosted_01

Después de comparar esta aplicación nueva y la anterior, me di cuenta de que tenía un malentendido sobre cómo el SP.RequestExecutor esperaba que las URL se construyeran. Pensé que era necesario utilizar el punto final SP.AppContextSite ().

Estaba construyendo incorrectamente una solicitud a la appWeb con una url similar a la siguiente:

https://contoso-6f921c6addc19f.sharepoint.com/ProviderHostedApp/_api/SP.AppContextSite(@target)/contextinfo?@target=%27https%3A%2F%2Fcontoso-6f921c6addc19f.sharepoint.com%2FProviderHostedApp%27

Como puede ver, el @target se configuró en la URL de la aplicación web, pero al hacer una solicitud a la aplicación mediante el uso de RequestExecutor, no es necesario que lo haga. Es simplemente appweburl + "/ _api / contextinfo". Solo cuando realiza solicitudes de recursos existentes en el hostWeb, necesita usar AppContextSite y configurar @target.

Puede ver el código completo en la solución vinculada para obtener más detalles. He añadido una captura de pantalla de la solución.