Getting “EndPoint Not Found” error with WCF service deployed to SharePoint 2010 site

独自空忆成欢 提交于 2020-01-13 18:24:26

问题


I am trying to utilize the autocompleteextender from the ajaxcontroltoolkit in one of my sharepoint solutions (which requires an older version of the library since all of the recent ones require the .NET Framework 4.0) and unfortunately I can't use a page method in a UserControl, only in an actual page. Therefore I am left with only the one option which is to use a method from an actual web service. The problem is that I can't seem to figure out how to correctly deploy the service and configure the web.config. When I try to view the metadata information in the browser I get an error message saying "endpoint not found". However, when I try to enter the URL with the name of a method appended to the URL it actually finds AND executes the method returning JSON data -which obviously means that it can find my service. Also, I can use jQuery to call my service and return the JSON data -but I don't want to use jQuery for this project and would rather just keep everything pure C#.

What I want to know is, why can't I get the "pretty metadata information" to display in the browser so I get the "warm fuzzy" and allow the proper discovery of the service by other applications the way it should be working?

I triple checked the web.config and I have both the metadataExchange endpoint as well as the one that references my service both entered and I have httpGetEnabled set to "true". I tried running svcutil /t:metadata and pointed it to my service but I get several errors, the first telling me that it can't obtain the metadata, the second that the Metadata contains a reference that cannot be resolved and the third is an HTTP GET Error that says the HTML document does not contain Web service discovery information. Even though I have my svc file in the same ISAPI directory as the OOTB SharePoint services and can generate the XSD files for those services but not for mine.

Here is what my current web.config file looks like...

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

I'm at a real loss here. Any thoughts?

Thanks in advance!!!

UPDATE #1: OK, I almost got it working. I mean I "technically" did but only in the browser and with a few caveats.

The first thing I did was add the following code to my web.config file:

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

And then updated my service endpoint to include a reference to the new configuration like so:

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

I also changed the binding from basicHttpBinding to webHttpBinding.

Now for the caveats...

  1. I can get it to fully function and return an XML response in the browser but ONLY if I run the service in debug mode, which means that it's being hosted in Visual Studio and not IIS so it doesn't really count.

  2. When I don't run the service in debug mode I get an error that says something to the effect of "The exception message is 'Value cannot be null. Parameter name: source'".

After stepping through my code in the debugger I see the real error which is:

Request for the permission of type 'IBM.Data.DB2.DB2Permission, IBM.Data.DB2, Version=9.0.0.2, Culture=neutral, PublicKeyToken=7c307b91aa13d208' failed.

Which in simple English means that I am trying to load a 32-Bit version of the IBM.Data.DB2 DLL in my SharePoint site that unfortunately only allows 64-Bit assemblies.

So my first reaction is "Ah ha!" this should be simple right? I mean I either need to find a 64bit version of the DLL or just configure the Application Pool that my SharePoint site in IIS uses to allow 32bit assemblies to be loaded and everything should be peachy! Aren't I smart? But NO says SharePoint! Not only am I not going to let you do either of those things* but I'm going to completely render your entire SharePoint farm unusable for even attempting to implement such ludicrous solutions!!!

So now when I try to navigate to any resource within my SharePoint farm I'm greeted with this.

[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

Even if I undo all of my changes to IIS, the App Pool, the DLLs, restart IIS, restart my server etc. nothing works. I'm just totally screwed.

Good times.

*Well to be fair SharePoint had nothing to do with the 64-Bit IBM driver being unavailable but I'm just gonna assume that even if there was one that I could techically use that SharePoint would find a way to punish me for trying to get any work done.

UPDATE #2: OK, I fixed the whole "SharePoint site not working thing". Basically the Application Pools in use for all SharePoint related sites seemed to have changed to enable 32-Bit applications, including the SecurityTokenServiceApplication (kudos to the dude over at this site for not only having the same problem but also posting the solution). Now while I don't remember specifically setting the value to true for every SharePoint related Application Pool I'm willing to play along and say that perhaps in an act of desperation I may have just said to hell with it and started recklessly changing configuration settings. You know, because that's just what seasoned developers do.

Anyhow...

Now my Central Administration website is back up and running but the UserProfileApplication service is still wonky and I am still inclined to think that my SecurityTokenServiceApplication service is still on the fritz since I get an error (details hidden of course since the web.config is configured to do just that) telling me that "The server was unable to process the request due to an internal error".

Details to follow...

UPDATE #3: OK, I got everything back to it's original working form. I confirmed that merely enabling 32-Bit applications for one Application Pool used by a SharePoint resource effectively updates all the other ones to follow suit. I know this for a fact because I positively did not change that value for EVERY SharePoint related Application Pool myself. I would have remembered going through something like that. As it turns out all of those Application Pools with the funky looking "GUID-like" names that the SharePoint Web Services use were also all updated to allow 32-Bit applications to run which is what caused my environment to go all screwy.

In addition, I got my SecurityTokenService to display it's metadata in the browser correctly by following the suggestions provided here. Although they seem like a bit of a "hack", they are small and reversable hacks so I'm game.

In short, you edit the config files for each service like so:

  1. In the spStsActAsBinding binding rename httpTransport to httpsTransport.
  2. Add allowInsecureTransport=”true” and enableUnsecuredResponse=”true” to the binding security.
  3. Ensure that you define only 1 binding configuration (for example in Profile Service, you will find 2 binding configuration for the same service each for http and https protocol).

After doing all this you should be able to get the "warm metadata fuzzy" that I'm trying to get for the custom WCF service I'm working on.

So now I just need to get back to fixing my initial problem which was to get the 32-Bit version of the IBM.Data.DB2 DLL to work with SharePoint 2010. The one thing I haven't tried yet is creating an IIS hosted service that runs COMPLETELY independent of SharePoint. Doing this would allow me to create a separate Application Pool that CAN in fact run 32-Bit applications without causing SharePoint to flip out -hopefully.

UPDATE #4: OK, so first of all I am going to tell you to ignore my advice above about messing around with the SecurityTokenService. Sure you get the "warm metadata fuzzy" with the changes but then you'll probably cause your SharePoint site's to break.

So anyway...I have now created a site that is totally independent of SharePoint that uses it's own Application Pool that is configured to allow 32-Bit applications so that technically should take care of all of my issues. Well, when I try typing text into my textbox it does not "autocomplete" but there is network activity happening because I see it in Fiddler. The strange part is that it only happens once. Meaning, if I hit backspace and retype something else no additional calls are made to the web service. I have to reload the page to test it out again. The error message I get when it does call the method is "405 Method Not Allowed". Now my method interface signature uses WebInvoke with the Method property set to "GET" which I've been told should work fine and has in the past when making REST calls that return JSON data, only now it doesn't work. Oh, in the web browser it works and I see wonderful XML results returned from the method. But when I try to use the AutoCompleteExtender in my SharePoint UserControl to call the method (or using any other client for that matter) it doesn't. I have tried using the WebGet() attribute but then nothing is returned in the web browser either. I think I got a 415 error for that one.

UPDATE #5: I found another post where some guy seems to be having the exact same problem that I am having now, only difference is that I can't get the autocomplete to work even if I have the webservice in the same project. I also tried the suggestions that were made in his post and they didn't work either. Here is a link to the post for those interested.


回答1:


For webpart project (with ASCX) you can:

1) Enable Anonymous Authentication for SP SIte in IIS

2) Add SP Mapped folder ISAPI

3) Create YourService.asmx in ISAPI

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

4) Edit user control code:

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) Edit your service code:

[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) Use you web service methods in control javascript:

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

No webconfig

For svc in ISAPI subfolder

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

web.config can be:

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



回答2:


So I still can't get the autocompleteextender to work with my WCF service but I did figure out how to fix the EndPoint Not Found error.

So there are three things at play here that I didn't fully understand or overlooked.

  1. The WCF Client Test Utility from Microsoft only tests SOAP requests.
  2. How all of the different bindings worked and what they meant.
  3. That you need to make sure the right account is being used for the Anonymous User Identity in IIS.

With items #1 and #2 I needed to add another endpoint to handle soap requests that either used basichttpbinding or wshttpbinding. I had only one binding specified that was webhttpbinding and although that gave me the warm fuzzies in the browser it essentially caused the WCF Client Utility not to work because I basically didn't have any SOAP endpoints defined. Read more about the differences here.

As for item #3, I found my answer here. It took days of weaving through posts about changing the web.config, writing stuff in code with usernames and passwords to enabling and disabling every form of authentication type known to man in IIS. This was what ultimately did the trick and allowed me to use the WCF Client Test Utility without the pesky EndPoint Not Found error.

Also, for those of you that are interested, I've actually started a new post that focuses specifically on the ajaxcontroltookkit autocompleteextender issue that can be found here.

FYI: I have managed to get to the point where I can return identical JSON data from my WCF service that looks just like the JSON data that gets returned when the code is executed from the ASPX's CodeBehind page and actually works...but for some reason when using the WCF method nothing gets autocompleted. The only thing I notice that's different when using the WCF service method is that before getting the sweet http 200 message with the JSON data there is always an http 401 authentication error that precedes it.



来源:https://stackoverflow.com/questions/15163342/getting-endpoint-not-found-error-with-wcf-service-deployed-to-sharepoint-2010

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!