SPContext.Current null al acceder a Sharepoint WCF utilizando AJAX
sharepoint-2010 claims (2)
Descubrí la solución de este problema, por lo que quiero compartir mis hallazgos en caso de que alguien más tropezara con este tipo de problema.
El problema básico es causado por el hecho de que las fábricas svc de Microsoft no pueden agregar enlaces de acceso adecuados para las llamadas ajax. Esto significa que la magia de autentificación de la carpeta vti_bin no sucederá. Obtiene un svc en ejecución, pero no tiene contexto Sharepoint cuando accede a él con llamadas ajax desde su javascript aunque el acceso ordinario funciona bien.
Puede solucionar el problema extendiendo Factory para reemplazar las vinculaciones con las correctas
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$"%>
<%@ServiceHost Language="C#" Debug="true"
Service="Driftportalen.LvService.SuggestService, $SharePoint.Project.AssemblyFullName$"
Factory="Driftportalen.LvService.AjaxCompatibleRestServiceHostFactory,Driftportalen.LvService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ab8de4d18e388c1f"
%>
La implementación de la nueva fábrica es la siguiente
public class AjaxCompatibleRestServiceHostFactory : Microsoft.SharePoint.Client.Services.MultipleBaseAddressBasicHttpBindingServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new AjaxCompatibleRestServiceHost(serviceType, baseAddresses);
}
}
Y, finalmente, el código actual para reemplazar las fijaciones
public class AjaxCompatibleRestServiceHost : Microsoft.SharePoint.Client.Services.MultipleBaseAddressWebServiceHost
{
public AjaxCompatibleRestServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
}
protected override void OnOpening()
{
base.OnOpening();
foreach (ServiceEndpoint endpoint in base.Description.Endpoints)
{
if (((endpoint.Binding != null) && (endpoint.Binding.CreateBindingElements().Find<WebMessageEncodingBindingElement>() != null)) && (endpoint.Behaviors.Find<WebScriptEnablingBehavior>() == null))
{
// try remove any previous behaviours
while (endpoint.Behaviors.Count > 0)
{
endpoint.Behaviors.RemoveAt(0);
}
endpoint.Behaviors.Add(new WebHttpBehavior());
}
}
ServiceDebugBehavior debug = this.Description.Behaviors.Find<ServiceDebugBehavior>();
// if not found - add behavior with setting turned on
if (debug == null)
{
this.Description.Behaviors.Add(
new ServiceDebugBehavior() { IncludeExceptionDetailInFaults = true });
}
else
{
// make sure setting is turned ON
if (!debug.IncludeExceptionDetailInFaults)
{
debug.IncludeExceptionDetailInFaults = true;
}
}
ServiceMetadataBehavior metadata =this.Description.Behaviors.Find<ServiceMetadataBehavior>();
// if not found - add behavior with setting turned on
if (metadata == null)
{
this.Description.Behaviors.Add(
new ServiceMetadataBehavior() { HttpGetEnabled = true });
}
else
{
// make sure setting is turned ON
if (!metadata.HttpGetEnabled)
{
metadata.HttpGetEnabled = true;
}
}
}
}
Es posible que desee utilizar WebScriptEnablingBehavior en lugar de WebHttpBehavior si desea que el resultado se complete cuando los recupere.
Hay algunas cosas adicionales a considerar. Existe alguna indicación en la red de que la falta de SP1 también puede dar como resultado la falta de contexto Sharepoint, por lo que debe verificar que tenga los últimos service packs si tiene problemas.
Finalmente, en caso de que esté creando un servicio REST, podría ser tentador usar UriTemplate para obtener la estructura de URL que desee. Lamentablemente, en el momento de escribir este documento UriTemplate no es compatible con Microsoft en este escenario, por lo que debe investigar este tema antes de basar su diseño en la existencia de UriTemplate.
Quiero configurar un WCF que pueda ser llamado por AJAX desde un javascript cargado desde un sitio web personalizado en nuestro sitio Sharepoint Foundation 2010. Para simplificar el procesamiento en el lado de Javascript, quiero presentar un servicio tranquilo que devuelva a Json a la persona que llama.
El problema es que cuando llamo al servidor con una llamada AJAX, SPContext.Current es nulo.
Estoy utilizando MultipleBaseAddressWebServiceHostFactory en el archivo svc para crear el servicio web
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$"%>
<%@ServiceHost Language="C#" Debug="true"
Service="Driftportalen.LvService.SuggestService"
Factory="Microsoft.SharePoint.Client.Services.MultipleBaseAddressWebServiceHostFactory, Microsoft.SharePoint.Client.ServerRuntime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
%>
El contrato para el servicio web es:
[ServiceContract(Namespace = "", ProtectionLevel= ProtectionLevel.None)]
public interface ISuggestServiceTest
{
[WebGet(UriTemplate = "/SuggestAddress/{streetprefix}/", ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
Dictionary<string, GenericAddress> SuggestAddress(string streetprefix);
}
La implementación del servicio web es básicamente la siguiente.
[Guid("BA6733B3-F98D-4AD8-837D-7673F8BC527F")]
[BasicHttpBindingServiceMetadataExchangeEndpoint]
[ServiceBehavior(IncludeExceptionDetailInFaults = true, AddressFilterMode = AddressFilterMode.Any)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class SuggestService : ISuggestServiceTest
{
private SPWeb currentWeb;
public SPWeb CurrentWeb
{
get
{
if (currentWeb == null)
{
var siteUrl = SPContext.Current.Web.Url;
SPSecurity.RunWithElevatedPrivileges(delegate
{
using (var site = new SPSite(siteUrl))
using (var web = site.OpenWeb())
{
currentWeb = web;
}
});
}
return currentWeb;
}
}
public Dictionary<string, GenericAddress> SuggestAddress(string streetprefix)
{
LvService lvService = new LvService(CurrentWeb);
Dictionary<string, GenericAddress> suggestions = new Dictionary<string, GenericAddress>();
//SNIP
//Code that uses lvService to populate suggestions
return suggestions;
}
}
He verificado que si llamo al servicio web desde el navegador web todo funciona como se espera y que recupero los datos correctos.
Uso la siguiente llamada Ajax
$.ajax({
url: addressUrl + "/"+request.term,
dataType: ''json'',
success: function (data) {
responseCallback(data);
$(this).removeClass("fetching");
}
});
Usando Firebug he verificado que se llama a la URL correcta desde el javascript y he verificado en el servidor que efectivamente se ha alcanzado el código correcto, pero SPContext.Current es nulo.
El servidor Sharepoint usa Windows y Reclamaciones para iniciar sesión. Esto significa que el WCF real se ejecutará usando una cuenta diferente a la solución de Sharepoint, pero desde que implemente en una carpeta debajo de vti_bin, Sharepoint debe proporcionar su contexto al WCF. Me parece que la llamada AJAX no activará Sharepoint para proporcionar su contexto, en cierto sentido es anónimo.
Al principio, asumí que el servicio web en sí era el culpable, ya que fallaría al azar cuando se llama desde la nave de búsqueda, pero creo que resolví ese problema instalando una actualización a Sharepoint Foundation 2010.
¿Cómo puedo hacer una llamada AJAX desde el servicio web javascript / a que acepta llamadas AJAX de Javascript que permiten que el servicio web acceda al contexto del usuario que se ha registrado en el sitio Sharepoint?
Me enfrenté al mismo problema con SharePoint 2013. No he tenido la oportunidad de probar su solución, porque he leído acerca de otra que básicamente obtiene el sitio actual y la identificación web antes de elevar los privilegios y luego recrearlos después. Algo como esto: https://sharepoint.stackexchange.com/questions/74205/spcontext-current-is-null-for-ihttphandler
SIN EMBARGO, por accidente, descubrí lo siguiente. Después de obtener el ID del sitio actual de esta manera: var currentSiteId = SPContext.Current.Site.ID;
¡SPContext.Current ya no es nulo más tarde mientras está dentro del delegado de privilegios elevados! ¡Parece que acceder a esta propiedad antes de elevar los privilegios también la hace disponible después! ¡¡¡¡Extraño!!!!
¡De hecho, la única línea de código que agregué fue la que ves arriba, sin usar esa variable más adelante! ¡Tengo la sensación de que hay una trampa en alguna parte!