3

我正在尝试在我的一个点解决方案中使用来自扩展器(这需要旧版本的库,因为所有最近的库都需要 .NET Framework 4.0),不幸的是我不能在UserControl,仅在实际页面中. 因此,我只剩下一个选择,即使用来自实际 Web 服务的方法。问题是我似乎无法弄清楚如何正确部署服务和配置 web.config。当我尝试在浏览器中查看元数据信息时,我收到一条错误消息,提示“找不到端点”。但是,当我尝试输入附加到 URL 的方法名称的 URL 时,它实际找到并执行返回 JSON 数据的方法 - 这显然意味着它可以找到我的服务。此外,我可以使用 jQuery 来调用我的服务并返回 JSON 数据——但我不想在这个项目中使用 jQuery,而宁愿保持一切纯 C#。

我想知道的是,为什么我不能让“漂亮的元数据信息”显示在浏览器中,所以我得到了“温暖的模糊”,并允许其他应用程序以它应该工作的方式正确发现服务?

我对 web.config 进行了三次检查,我同时输入了 metadataExchange 端点以及引用我的服务的端点,并且我将 httpGetEnabled 设置为“true”。我尝试运行 svcutil /t:metadata 并将其指向我的服务,但出现了几个错误,第一个告诉我它无法获取元数据,第二个是元数据包含无法解析的引用,第三个是HTTP GET 错误,表示 HTML 文档不包含 Web 服务发现信息。即使我的 svc 文件与 OOTB SharePoint 服务位于同一个 ISAPI 目录中,并且可以为这些服务生成 XSD 文件,但不能为我的服务生成 XSD 文件。

这是我当前的 web.config 文件的样子......

<?xml version="1.0"?>
 <configuration>
  <system.web>
   <compilation debug="true" />
    <identity impersonate="true"/>
  </system.web>
 <system.serviceModel>
 <!-- we need this to enable session -->
 <client>
 </client>
 <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
 <behaviors>
  <endpointBehaviors>
  </endpointBehaviors>
  <serviceBehaviors>
    <behavior name="metadataSupportBehavior">
      <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
      <serviceMetadata httpGetEnabled="true" httpGetUrl="http://myserver/sites/mysitecollection/_vti_bin/WebServices/MyService.svc" policyVersion="Policy15"/>
      <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
      <serviceDebug includeExceptionDetailInFaults="true"/>
      <serviceAuthorization impersonateCallerForAllOperations="false"/>
      <serviceCredentials>
        <windowsAuthentication includeWindowsGroups="true" />
      </serviceCredentials>
    </behavior>
    <behavior name="defaultBehavior">
      <serviceDebug includeExceptionDetailInFaults="true" />
      <serviceMetadata httpGetEnabled="true" />
    </behavior>
  </serviceBehaviors>
</behaviors>
<bindings>
</bindings>
<services>
  <service behaviorConfiguration="metadataSupportBehavior" name="Sample.WebServices.MyService">
    <clear />
    <endpoint address="" binding="basicHttpBinding" contract="Sample.WebServices.IMyService">
    </endpoint>
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange">
      <identity>
        <certificateReference storeName="My" storeLocation="LocalMachine"
          x509FindType="FindBySubjectDistinguishedName" />
      </identity>
    </endpoint>
    <host>
      <baseAddresses>
        <add baseAddress="http://myserver/sites/mysitecollection/_vti_bin/WebServices/MyService.svc" />
      </baseAddresses>
    </host>
  </service>
</services>

我在这里真是不知所措。有什么想法吗?

提前致谢!!!

更新#1:好的,我几乎让它工作了。我的意思是我“技术上”做了,但只是在浏览器中并有一些警告。

我做的第一件事是将以下代码添加到我的 web.config 文件中:

  <endpointBehaviors>
    <behavior name="webBehavior">
      <webHttp />
    </behavior>
  </endpointBehaviors>

然后更新我的服务端点以包含对新配置的引用,如下所示:

    <endpoint address="" binding="webHttpBinding" contract="Sample.WebServices.IMyService" behaviorConfiguration="webBehavior">
      <identity>
        <dns value="localhost" />
      </identity>
    </endpoint>

我还将绑定从basicHttpBinding更改为webHttpBinding

现在警告...

  1. 我可以让它完全发挥作用并在浏览器中返回 XML 响应,但前提是我在调试模式下运行该服务,这意味着它托管在 Visual Studio 而不是 IIS 中,因此它并不真正重要。

  2. 当我不在调试模式下运行服务时,我收到一条错误消息,说明“异常消息是‘值不能为空。参数名称:源’”。

在调试器中单步执行我的代码后,我看到了真正的错误:

请求“IBM.Data.DB2.DB2Permission, IBM.Data.DB2, Version=9.0.0.2, Culture=neutral, PublicKeyToken=7c307b91aa13d208”类型的权限失败。

简而言之,这意味着我正在尝试在我的 SharePoint 站点中加载 32 位版本的 IBM.Data.DB2 DLL,不幸的是,它只允许 64 位程序集。

所以我的第一反应是“啊哈!” 这应该很简单吧?我的意思是我要么需要找到 64 位版本的 DLL,要么只配置我在 IIS 中的 SharePoint 站点使用的应用程序池,以允许加载 32 位程序集,一切都应该是桃色的!我不聪明吗?但是没有说 SharePoint!我不仅不会让你做这些事情中的任何一个*,而且我会让你的整个 SharePoint 场完全无法使用,甚至无法尝试实施这种可笑的解决方案!!!

所以现在,当我尝试导航到我的 SharePoint 场中的任何资源时,我会受到欢迎。

[NullReferenceException: Object reference not set to an instance of an object.]
   Microsoft.Office.Server.Administration.UserProfileApplicationProxy.get_ApplicationProperties() +134
   Microsoft.Office.Server.Administration.UserProfileApplicationProxy.get_PartitionIDs() +44
   Microsoft.Office.Server.Administration.UserProfileApplicationProxy.IsAvailable(SPServiceContext serviceContext) +329
   Microsoft.Office.Server.WebControls.MyLinksRibbon.get_PortalAvailable() +44
   Microsoft.Office.Server.WebControls.MyLinksRibbon.EnsureMySiteUrls() +60
   Microsoft.Office.Server.WebControls.MyLinksRibbon.get_PortalMySiteUrlAvailable() +15
   Microsoft.Office.Server.WebControls.MyLinksRibbon.OnLoad(EventArgs e) +91
   System.Web.UI.Control.LoadRecursive() +65
   System.Web.UI.Control.LoadRecursive() +190
   System.Web.UI.Control.LoadRecursive() +190
   System.Web.UI.Control.LoadRecursive() +190
   System.Web.UI.Control.LoadRecursive() +190
   System.Web.UI.Control.LoadRecursive() +190
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2427

即使我撤消对 IIS、应用程序池、DLL 的所有更改,重新启动 IIS,重新启动我的服务器等,也没有任何效果。我完全搞砸了。

美好时光。

*公平地说,SharePoint 与 64 位 IBM 驱动程序不可用无关,但我只是假设即使有一个我可以在技术上使用的,SharePoint 也会找到一种方法来惩罚我试图获取完成的任何工作。

更新#2:好的,我修复了整个“SharePoint 站点不工作”。基本上,所有与 SharePoint 相关的网站使用的应用程序池似乎都已更改为启用 32 位应用程序,包括 SecurityTokenServiceApplication(感谢网站上的老兄不仅有同样的问题,而且还发布了解决方案)。现在,虽然我不记得为每个与 SharePoint 相关的应用程序池专门设置了该值,但我愿意一起玩,并说也许在绝望的情况下,我可能刚刚对它说地狱并开始鲁莽地更改配置设置. 你知道,因为这正是经验丰富的开发人员所做的。

无论如何...

现在我的中央管理网站已备份并运行,但 UserProfileApplication 服务仍然不稳定,我仍然倾向于认为我的 SecurityTokenServiceApplication 服务仍然处于崩溃状态,因为我收到错误(由于配置了 web.config,细节当然隐藏了这样做)告诉我“由于内部错误,服务器无法处理请求”。

详情请关注...

更新#3:好的,我把一切都恢复到原来的工作形式。我确认,仅为 SharePoint 资源使用的一个应用程序池启用 32 位应用程序会有效地更新所有其他应用程序以效仿。我知道这是一个事实,因为我自己确实没有为每个与 SharePoint 相关的应用程序池更改该值。我会记得经历过类似的事情。事实证明,SharePoint Web 服务使用的所有具有时髦外观的“类似 GUID”的名称的应用程序池也都已更新为允许运行 32 位应用程序,这导致我的环境变得一团糟。

此外,按照此处提供的建议,我让我的 SecurityTokenService 在浏览器中正确显示它的元数据。虽然它们看起来有点像“黑客”,但它们是小型且可逆的黑客,所以我很喜欢。

简而言之,您可以像这样编辑每个服务的配置文件:

  1. 在 spStsActAsBinding 绑定中,将 httpTransport 重命名为 httpsTransport。
  2. 将 allowInsecureTransport=”true” 和 enableUnsecuredResponse=”true” 添加到绑定安全性。
  3. 确保您只定义了 1 个绑定配置(例如,在 Profile Service 中,您会发现同一服务的 2 个绑定配置分别用于 http 和 https 协议)。

在完成所有这些之后,您应该能够获得我正在尝试为我正在处理的自定义 WCF 服务获取的“温暖元数据模糊”。

所以现在我只需要重新解决我最初的问题,即让 32 位版本的 IBM.Data.DB2 DLL 与 SharePoint 2010 一起使用。我还没有尝试过的一件事是创建一个托管的 IIS完全独立于 SharePoint 运行的服务。这样做可以让我创建一个单独的应用程序池,它实际上可以运行 32 位应用程序,而不会导致 SharePoint 崩溃——希望如此。

更新#4:好的,所以首先我要告诉你忽略我上面关于搞乱 SecurityTokenService 的建议。当然,您会通过更改获得“温暖的元数据模糊”,但您可能会导致您的 SharePoint 网站中断。

所以无论如何...我现在创建了一个完全独立于 SharePoint 的网站,它使用自己的应用程序池,该应用程序池配置为允许 32 位应用程序,因此从技术上讲应该可以解决我的所有问题。好吧,当我尝试在我的文本框中输入文本时,它不会“自动完成”,但是会发生网络活动,因为我在 Fiddler 中看到了它。奇怪的是它只发生一次。这意味着,如果我按退格键并重新输入其他内容,则不会对 Web 服务进行额外调用。我必须重新加载页面以再次测试它。我在调用该方法时收到的错误消息是“405 Method Not Allowed”。现在我的方法接口签名使用 WebInvoke 并将 Method 属性设置为“GET”,我' 已经被告知应该可以正常工作,并且过去在进行返回 JSON 数据的 REST 调用时,现在它不起作用。哦,在 Web 浏览器中它可以工作,我看到该方法返回了美妙的 XML 结果。但是,当我尝试在 SharePoint UserControl 中使用 AutoCompleteExtender 来调用该方法(或为此使用任何其他客户端)时,它不会。我曾尝试使用 WebGet() 属性,但 Web 浏览器中也没有返回任何内容。我想我收到了一个 415 错误。我曾尝试使用 WebGet() 属性,但 Web 浏览器中也没有返回任何内容。我想我收到了一个 415 错误。我曾尝试使用 WebGet() 属性,但 Web 浏览器中也没有返回任何内容。我想我收到了一个 415 错误。

更新#5:我发现了另一个帖子,其中有些人似乎遇到了与我现在遇到的完全相同的问题,唯一的区别是即使我在同一个项目中有网络服务,我也无法让自动完成工作。我也尝试了他帖子中提出的建议,但它们也没有奏效。这是帖子的链接,供感兴趣的人使用。

4

2 回答 2

1

对于 webpart 项目(使用 ASCX),您可以:

1) 在 IIS 中为 SP 站点启用匿名身份验证

2)添加SP映射文件夹ISAPI

3) 在 ISAPI 中创建 YourService.asmx

<%@ WebService Language="C#" Class="YourNamespace.YourService, YourAsm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=YourPublicKeyToken"%>

4) 编辑用户控制代码:

public partial class YourControl : UserControl
{
    protected override void OnInit(EventArgs e)
    {
        base.OnInit(e);
        FixFormAction();
        CheckScriptManager();           
        EnsureUpdatePanelFixups();
    }

    private void FixFormAction()
    {
        // Fix problem with postbacks and form actions (DevDiv 55525)
        Page.ClientScript.RegisterStartupScript(GetType(), ID, "_spOriginalFormAction = document.forms[0].action;", true);

        // By default, the onsubmit event for forms in SharePoint master pages call "return _spFormOnSubmitWrapper()" 
        // which blocks async postbacks after the first one.   Not calling "_spFormOnSubmitWrapper()" breaks all postbacks
        // and defeats the purpose of _spFormOnSubmitWrapper() which is to block repetitive postbacks.  
        // To call _spFormOnSubmitWrapper() and allow partial postbacks, remove "return" from the original call.  
        if (Page.Form != null)
        {
            string formOnSubmitAtt = Page.Form.Attributes["onsubmit"];
            if (formOnSubmitAtt == "return _spFormOnSubmitWrapper();")
            {
                Page.Form.Attributes["onsubmit"] = "_spFormOnSubmitWrapper();";
            }
        }
    }

    private void EnsureUpdatePanelFixups()
    {
        if (this.Page.Form != null)
        {
            var fixupScript = @"
            _spBodyOnLoadFunctionNames.push(""_initFormActionAjax"");
            function _initFormActionAjax()
            {
            if (_spEscapedFormAction == document.forms[0].action)
            {
            document.forms[0]._initialAction = document.forms[0].action;
            }
            }
            var RestoreToOriginalFormActionCore = RestoreToOriginalFormAction;
            RestoreToOriginalFormAction = function()
            {
            if (_spOriginalFormAction != null)
            {
            RestoreToOriginalFormActionCore();
            document.forms[0]._initialAction = document.forms[0].action;
            }
            }
            ";
            ScriptManager.RegisterStartupScript(this, this.GetType(), "EnsureUpdatePanelFixup", fixupScript, true);
        }
    }

    private ScriptManager CheckScriptManager()
    {
        ScriptManager sm = ScriptManager.GetCurrent(Page);
        if (sm == null)
        {
            if (Page.Form != null)
            {
                sm = new ScriptManager();
                sm.ID = Page.Form.ID + "_ScriptManager";
                Page.Form.Controls.Add(sm);
                //Page.Form.Controls.AddAt(0, sm);  
            }
        }
        sm.EnablePageMethods = true;

        var sharedPath = @"~/_layouts/Share/";
        var path = @"~/_layouts/YourWebPart/";

        sm.Scripts.Add(new ScriptReference { Path = @"/_vti_bin/YourService.asmx/JS" });

        //Registering ExtJS
        Page.ClientScript.RegisterClientScriptInclude(GetType(), "ext-all", sharedPath + "scripts/ext-all.js");

        ScriptManager.RegisterClientScriptBlock(this, GetType(), "ext-css",
                                    "<link href='" +
                                    sharedPath + "resources/css/ext-all.css' rel='stylesheet' type='text/css' />", false);

        Page.ClientScript.RegisterClientScriptInclude(GetType(), "YourScript", path + "scripts/Script.js");

        return sm;
    }

}

5) 编辑您的服务代码:

[WebService(Namespace = "http://tempuri/ws")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class YourService : WebService
{
    //[WebMethod]
    [ScriptMethod]
    [WebMethod(EnableSession = true)]
    //[ScriptMethod(UseHttpGet = true)]
    public string YourWebMethod(string arg)
    {           
        return arg;
    }
}

6)在控制javascript中使用你的网络服务方法:

YourNamespace.YourService.YourWebMethod(arg, function (result) {
        if (result) {
            if (typeof result == 'string') {
                alert(result);
            }
        }
    }, function (error) { alert('YourWebMethod failed! ' + error.get_message()); });
}

没有网络配置

对于 ISAPI 子文件夹中的 svc

<%@ ServiceHost Debug="true" Language="C#"
Service="Namespace.MyService, MyAsm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=MyPublicKeyToken"
%>

web.config 可以是:

<system.webServer>    
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="2097151"/>
      </requestFiltering>
    </security>
  </system.webServer>
  <system.web>
    <httpRuntime executionTimeout="60" maxRequestLength="2097151" />        
  </system.web>  
  <system.serviceModel>    
    <bindings>
      <basicHttpBinding>        
        <binding name="MyHttpBinding"
         maxBufferPoolSize="2147483647"
         maxBufferSize="2147483647"
         maxReceivedMessageSize="2147483647">          
          <security mode="TransportCredentialOnly">
            <transport clientCredentialType="Ntlm" proxyCredentialType="None" realm=""/>
            <message clientCredentialType="UserName" algorithmSuite="Default"/>
          </security>          
          <readerQuotas maxArrayLength="2147483647" maxDepth="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" maxStringContentLength="2147483647"/>
        </binding>
      </basicHttpBinding>      
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="CustomServiceBehaviour">          
          <serviceMetadata httpGetEnabled="true"/>          
          <serviceDebug includeExceptionDetailInFaults="false"/>
          <dataContractSerializer maxItemsInObjectGraph="2147483647"/>          
        </behavior>
      </serviceBehaviors>      
    </behaviors>    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
    <services>
      <service behaviorConfiguration="CustomServiceBehaviour" name="Namespace.MyService">        
        <endpoint address="" binding="basicHttpBinding" bindingConfiguration="MyHttpBinding" contract="Namespace.IMyService" bindingNamespace="http://tempuri/ws" />
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>      
    </services>
    </system.serviceModel>
于 2013-03-01T20:48:05.977 回答
0

所以我仍然无法让与我的 WCF 服务一起使用,但我确实想出了如何修复EndPoint Not Found错误。

所以这里有三件事我没有完全理解或忽视。

  1. Microsoft 的 WCF 客户端测试实用程序仅测试 SOAP 请求。
  2. 所有不同的绑定如何工作以及它们的含义。
  3. 您需要确保将正确的帐户用于 IIS 中的匿名用户身份。

对于项目#1#2,我需要添加另一个端点来处理使用的肥皂请求。我只指定了一个绑定,即,尽管这给了我浏览器中的温暖模糊,但它本质上导致 WCF 客户端实用程序无法工作,因为我基本上没有定义任何 SOAP 端点。在此处阅读有关差异的更多信息。

至于第 3项,我在这里找到了答案。花了好几天的时间整理有关更改 web.config 的帖子,用用户名和密码编写代码以启用和禁用 IIS 中人类已知的每种形式的身份验证类型。这就是最终的诀窍,让我可以使用 WCF 客户端测试实用程序而不会出现讨厌的EndPoint Not Found错误。

另外,对于那些感兴趣的人,我实际上已经开始了一篇新文章,专门关注 问题,可以在这里找到。

仅供参考:我已经设法从我的 WCF 服务返回相同的JSON数据,这些数据看起来就像从 ASPX 的 CodeBehind 页面执行代码并且实际工作时返回的 JSON 数据......但对于某些人来说使用 WCF 方法时没有自动完成的原因。在使用 WCF 服务方法时,我注意到的唯一不同之处在于,在获取带有JSON数据的甜蜜 http 200 消息之前,总是会出现一个 http 401 身份验证错误。

于 2013-03-12T20:42:40.250 回答