I have a WCF service with the following configuration:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MetadataEnabled">
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MetadataEnabled" name="MyNamespace.MyService">
<endpoint name="BasicHttp"
address=""
binding="basicHttpBinding"
contract="MyNamespace.IMyServiceContract" />
<endpoint name="MetadataHttp"
address="contract"
binding="mexHttpBinding"
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost/myservice" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
When hosting the service in the WcfSvcHost.exe process, if I browse to the URL:
http://localhost/myservice/contract
where the service metadata is available I get an HTTP 400 Bad Request error.
By inspecting the WCF logs I found out that an System.Xml.XmlException exception is being thrown with the message: "The body of the message cannot be read because it is empty."Here is an extract of the log file:
<Exception>
<ExceptionType>
System.ServiceModel.ProtocolException, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
</ExceptionType>
<Message>There is a problem with the XML that was received from the network. See inner exception for more details.</Message>
<StackTrace>
at System.ServiceModel.Channels.HttpRequestContext.CreateMessage()
at System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(HttpRequestContext context, ItemDequeuedCallback callback)
at System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContextCore(IAsyncResult result)
at System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContext(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.ListenerAsyncResult.WaitCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
</StackTrace>
<InnerException>
<ExceptionType>System.Xml.XmlException, System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>The body of the message cannot be read because it is empty.</Message>
<StackTrace>
at System.ServiceModel.Channels.HttpRequestContext.CreateMessage()
at System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(HttpRequestContext context, ItemDequeuedCallback callback)
at System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContextCore(IAsyncResult result)
at System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContext(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.ListenerAsyncResult.WaitCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
</StackTrace>
</InnerException>
</Exception>
If I instead browse to the URL:
http://localhost/myservice?wsdl
everything works just fine and I get the WSDL contract. At this point, I can also remove the "MetadataHttp" metadata endpoint completely, and it wouldn't make any difference.
I'm using .NET 3.5 SP1. Does anyone have an idea of what could be wrong here?
I think I found out what the problem is.
If I browse to the URL:
http://localhost/myservice/contract
with the WcfTestClient application I can successfully retrieve the service metadata.
So the error really only occurs when I request the URL through a web browser.
The HTTP Bad Request error comes from the fact that the browser issues an HTTP GET request where the contents of the message are in the HTTP headers, and the body is empty.This is exactly what the WCF mexHttpBinding is complaining about!
In order to get to the service contract via a web browser you will have to explicitly enable it in the service behavior:
<serviceBehaviors>
<behavior name="MetadataEnabled">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
The URL to request becomes then:
http://localhost/myservice?wsdl
So, it turns out I was a little too quick in posting this question. However, I'll keep it anyway just for the record.
Generally this is a problem with the size of the SOAP envelop. Check your binding configuration in order to change MaxBufferPoolSize, MaxReceivedMessageSize to allow huge contents. Remember, you must change both in client and server sides.
Another issue is the MessageEnconding (another binding parameter), ensure that client and server side are using the same encoding.
Finally, check the Reader Quotas Properties parameters.
I was able to fix the "400 Bad Request" problem by switching my WCF service from running off of Visual Studio Development Server to using Local IIS Web server (right click on the project --> properties --> web tab --> radio button under "Servers"). I hope this helps someone out there because it took me two days to figure this one out.
I had HTTP 400 issues when consuming a HTTPS mex URL from SvcUtil, eventhough httpsGetEnabled was set to true. The error message was miles away from what was really the issue so I'm posting here in case anyone else stumbles upon the same problem.
I had a self-signed CA certificate (TestRootCA) which was the issuer of the server certificate (localhost). On the client I imported the TestRootCA CER file but I didn't import the CRL (Certificate Revocation List). It seems that when you use a self-signed CA you must also import the CRL, otherwise server authentication fails in strange ways, none of which point you to the real problem. What's worse is that the fail happens during SSL handshake, before the request even reaches your service, so you'll see no errors in WCF trace logs.
If you are not sure why your WCF code is throwing an error then I highly recommend the MS Service Trace Viewer. It is very helpful in identifying communication and transport issues like this. Take a look at this C# Corner article for more details.
Related
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...
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.
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:
In the spStsActAsBinding binding rename httpTransport to httpsTransport.
Add allowInsecureTransport=”true” and enableUnsecuredResponse=”true” to the binding security.
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.
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>
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.
The WCF Client Test Utility from Microsoft only tests SOAP requests.
How all of the different bindings worked and what they meant.
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.
I'm using an encrypted timestamp to secure communications between a WCF service in my web server and another in the 'mid-tier'. In the calling service (the client in this exchange) I'm using a DLL whose class inherits System.ServiceModel.Configuration.BehaviorExtensionElement to provide functionality implementing System.ServiceModel.Description.IEndpointBehavior and System.ServiceModel.Dispatcher.IClientMessageInspector. This is glued in via the web.config file with the sections
<extensions>
<behaviorExtensions>
<add name="MessageInspectorBehavior" type="MessageInspectorDLL.MessageInspectorBehaviorExtensionElement, MessageInspectorDLL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<endpointBehaviors>
<behavior name="MessageInspectorEndpointBehavior">
<MessageInspectorBehavior />
</behavior>
</endpointBehaviors>
and then these are hooked up to the mid-tier WCF with entries like this one
<client>
<endpoint address="http://blah.cloudapp.net:8080/BlahService.svc"
behaviorConfiguration="MessageInspectorEndpointBehavior" binding="customBinding"
bindingConfiguration="CustomBinding_BlahService"
contract="BlahServiceReference.BlahService"
name="CustomBinding_BlahService" />
In the mid-tier there's a similar story with a System.ServiceModel.Dispatcher.IClientMessageInspector decrypting the time stamp and either processing the request or throwing a System.ServiceModel.FaultException with the error message "Invalid stamp".
This follows code examples at http://msdn.microsoft.com/en-us/library/ms733747.aspx and http://weblogs.asp.net/paolopia/archive/2007/08/23/writing-a-wcf-message-inspector.aspx
Now I have changed the mid-tier WCF. Some changes are simple (e.g. correcting a spelling mistake in a method name "retrive" -> "retrieve") while some adds more substantial functionality and I want to update the service recerences in the client. In VS2010 I right-click on the service references that will need updating and select "Update Service Reference". VS2010 connects to the mid-tier WCF ("downloading service information") and then throws the error
The configuration for the service reference could not be updated due to the following issue: The type 'MessageInspectorDLL.MessageInspectorExtensionElement, MessageInspectorDLL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' registered for extension 'MessageInspectorBehavior' could not be loaded. (C:\TFS\Blah\Blah\web.config line 80)
What's wrong? Is there something I need to set up for the update service reference to correctly use the message inspector DLL?
Though it is possible that the DLL or the web.config have errors that seems unlikely since the client will connect to the previous mid-tier which is up on Azure, and that has the same encrypted timestamp inspection.
I've found an answer. Adding the MessageInspectorDLL as a reference to the ASP application fixed it for me.
I am trying to host the same service inside the same website using two endpoints one HTTP and the other HTTPS.
The problem is I can happily call one service (whichever I call first works) but then call to the other fails until I iisreset and try again. SO I can happily call both but one at a time until a process recycle.
I have exhausted (or I think I have) all relevant Q&A and documents that I could find and none of them seem to help me. The level of documentation of MSDN regarding these cases (which should be faily common) is appalling and nothing works as it seems.
I have used Host/BaseAddresses/BaseAddress with no luck and whenever I used it, I get another error (cannot find an HTTPS scheme).
I have folder called Secure which is enabled for HTTPS inside IIS.
I have disabled mex so no metadata allowed which I got passed previous errors.
Anyone got a clue? I am cluless...
<service name="Namespace.MyService" behaviorConfiguration="MyBehaviour">
<host>
<baseAddresses>
</baseAddresses>
</host>
<endpoint address="http://localhost/Services/MyService.svc/MyService"
name="MyService" binding="wsHttpBinding"
bindingConfiguration="myWsHttpBinding" contract="Namespace.IMyService" />
<endpoint address="https://localhost/Services/Secure/MySslService.svc/MySslService"
name="MySslService" binding="basicHttpBinding"
bindingConfiguration="MySslServiceBinding" contract="Namespace.IMyService" />
</service>
.....
<behavior name="MyBehaviour">
<custom1/>
<custom2/>
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="CustomProvider"/>
<serviceCredentials>
<serviceCertificate findValue="Some" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="SomeClass, SomeDll"/>
</serviceCredentials>
</behavior>
Original error is:
A registration already exists for URI
'http://localhost/Services/....'.
Description: An unhandled exception
occurred during the execution of the
current web request. Please review the
stack trace for more information about
the error and where it originated in
the code.
Exception Details:
System.InvalidOperationException: A
registration already exists for URI
'http://localhost/Services/....'.
Source Error:
An unhandled exception was generated
during the execution of the current
web request. Information regarding the
origin and location of the exception
can be identified using the exception
stack trace below.
Stack Trace:
[InvalidOperationException: A
registration already exists for URI
System.ServiceModel.Channels.UriPrefixTable1.RegisterUri(Uri
uri, HostNameComparisonMode
hostNameComparisonMode, TItem item)
+320 System.ServiceModel.Channels.HttpTransportManager.Register(TransportChannelListener
channelListener) +380
System.ServiceModel.Channels.TransportManager.Open(TransportChannelListener
channelListener) +816
System.ServiceModel.Channels.TransportManagerContainer.Open(SelectTransportManagersCallback
selectTransportManagerCallback) +121
System.ServiceModel.Channels.HttpChannelListener.OnOpen(TimeSpan
timeout) +125
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan
timeout) +789
System.ServiceModel.Channels.DatagramChannelDemuxer2.OnOuterListenerOpen(ChannelDemuxerFilter
filter, IChannelListener listener,
TimeSpan timeout) +606
System.ServiceModel.Channels.SingletonChannelListener`3.OnOpen(TimeSpan
timeout) +91
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan
timeout) +789
System.ServiceModel.Dispatcher.ChannelDispatcher.OnOpen(TimeSpan
timeout) +375
Here is the WCF Trace log error:
The ChannelDispatcher at
'http://localhost/Services/...' with
contract(s) '"IMyService"' is unable
to open its IChannelListener.
System.ServiceModel.Dispatcher.ChannelDispatcher.OnOpen(TimeSpan
timeout)
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan
timeout)
System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan
timeout)
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan
timeout)
System.ServiceModel.ServiceHostingEnvironment.HostingManager.ActivateService(String
normalizedVirtualPath)
System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String
normalizedVirtualPath)
System.ServiceModel.ServiceHostingEnvironment.EnsureServiceAvailableFast(String
relativeVirtualPath)
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.HandleRequest()
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.BeginRequest()
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.OnBeginRequest(Object
state)
System.ServiceModel.AspNetPartialTrustHelpers.PartialTrustInvoke(ContextCallback
callback, Object state)
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.OnBeginRequestWithFlow(Object
state)
System.Runtime.IOThreadScheduler.ScheduledOverlapped.IOCallback(UInt32
errorCode, UInt32 numBytes,
NativeOverlapped* nativeOverlapped)
System.Runtime.Fx.IOCompletionThunk.UnhandledExceptionFrame(UInt32
error, UInt32 bytesRead,
NativeOverlapped* nativeOverlapped)
System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32
errorCode, UInt32 numBytes,
NativeOverlapped* pOVERLAP)
OK, I fixed my problem. The issue was I was under impression that I had to create a secure folder in IIS and put the .svc file in there to guarantee SSL transmission. It turned out that it is not the case and all I had to do was to use the same .svc file and just define 2 endpoints:
<endpoint address="http://localhost/Services/MyService.svc/MyService"
name="MyService" binding="wsHttpBinding"
bindingConfiguration="myWsHttpBinding" contract="Namespace.IMyService" />
<endpoint address="https://localhost/Services/MyService.svc/MySecureService"
name="MySslService" binding="basicHttpBinding"
bindingConfiguration="MySslServiceBinding" contract="Namespace.IMyService" />
It's possible the nested directories are confusing IIS - I have had this happen before although not in the WCF context. Try changing the endpoint addresses to:
address="http://localhost/MyService"
address="https://localhost/MySslService"
Can you try hosting the application from a console app instead of IIS? I'm pretty certain IIS is causing your problem somehow but this might confirm it.
If it's possible, I'd also try to isolate the problem by removing some of the configuration detail - eg bindingConfiguration and behaviorConfiguration.
Obviously these are just debugging steps I'd perform (you may have already) to try and isolate the problem - sorry I haven't got a full answer.
I have a wcf webHttp endpoint and noticed today that when called httpGet with a long url ~364 total characters (http:// etc counted) IIS throws a 400 bad request error. Throwing a breakpoint in the code never gets reached. If I remove some parameters and have a url that is 354 the web service runs as expected.
I'm not sure where the issue is since urls can be like 2k. I'm not posting any data so I don't think I'm hitting a 4mb limit like from here
What gives?
Here's some wcf xml stuff.
<behaviors>
<endpointBehaviors>
<behavior name="REST">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<endpoint address="" behaviorConfiguration="REST" binding="customBinding" bindingConfiguration="jsonpBinding" contract="Interface"/>
<bindings>
<customBinding>
<binding name="jsonpBinding">
<jsonpMessageEncoding/>
<httpTransport manualAddressing="true"/>
</binding>
</customBinding>
</bindings>
<extensions>
<bindingElementExtensions>
<add name="jsonpMessageEncoding" type="Microsoft.Ajax.Samples.JsonpBindingExtension, service, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</bindingElementExtensions>
</extensions>
I just removed the callback function name which significantly reduced the size of the url and it still threw a 400 error. This makes me think that there is a WCF size limit on the text that can be sent in as a string parameter.
here;'s some Contract stuff
[OperationContract]
[WebGet(UriTemplate = #"{userName}/layer({layerName})returnAttributes({attributeList})where({attributeName})({op})({attributeValue})", ResponseFormat = WebMessageFormat.Json)]
[JSONPBehavior(callback = "callback")]
DojoGrouping GetAttributes(string userName, string layerName, string attributeList, string attributeName, string attributeValue);
the issue is with attributeList which can be comma separated list.
so with a url call like
http://../demo/layer(OfficialsLookup)returnAttributes(USHOUSE,US_Photo,US_Web,US_Name,SENDIST,SEN_Name,SEN_Email,SEN_Party,SEN_Photo,REPDIST,REP_Name,REP_Email,REP_Party,REP_Web,REP_Photo)utmX(430)utmY(4502)
it busts. But if i shorten the return attribute text then it functions properly.
I've added I added the following entry into the registry:
Key Name: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HTTP\Parameters
Class Name: <NO CLASS>
Last Write Time: 1/25/2011 - 3:34 PM
Value 0
Name: UrlSegmentMaxLength
Type: REG_DWORD
Data: 0x200
I rebooted the machine after adding it and am still receiving the same error. Is this the correct spot to be modifying the HTTP.sys settings that WCF and IIS are using? Is there a way to test that WCF is being affected by this registry value?
Here is a related post with no answer but saying that the httpsys stuff did not fix a 64 bit server which is what we are using.
Related Post
To fix our issue we had to use the .NET 4.0 framework. We moved the web service to run under a .net 4.0 app pool and changed the target framework to 4.0. This caused the site to spit out url is too long error instead of just a bad request. After adding a web config entry in
<system.web>
added
<httpRuntime maxUrlLength="500" />
for max url length, the service is up and running as expected.
WCF uses HTTP.sys to handle HTTP traffic. HTTP.sys has system wide settings to handle various restrictions around URL. You might be hitting one of them.
See the following article to find out those settings:
http://support.microsoft.com/kb/820129
You will need to restart http service and your WCF service. If it is hosted in IIS, restart IIS. UrlSegmentMaxLength seems to be an interesting for your URI template.
I am consuming a web service internal to my company from ASP.NET. I've used svcutil.exe to connect to the service and generate the bindings and classes from the wsdl. I am able to connect to the dev version, which does not require authentication. Now we are adding in security. My new URI uses https but also requires user credentials.
I am very new to WCF, and am trying to determine the way to configure this. From my reading on MSDN, it appears that the way to go is using.
UPDATE:
Here is the most recent code I've been trying. This incorporates feedback from the answer(s):
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="svcBehavior">
<serviceCredentials>
<serviceCertificate storeLocation="CurrentUser"
storeName="My"
x509FindType="FindByThumbprint"
findValue="xx xx xx etc"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="CustomerPaymentProgramSOAPBinding">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="https://***URL***"
binding="wsHttpBinding" bindingConfiguration="CustomerPaymentProgramSOAPBinding"
contract="CppService.CustomerPaymentProgramService" name="CustomerPaymentProgramService">
</endpoint>
</client>
</system.serviceModel>
Here is the calling code:
using (var svc = new CustomerPaymentProgramServiceClient())
{
svc.ClientCredentials.UserName.UserName = "*******";
svc.ClientCredentials.UserName.Password = "*******";
var request = new GetServiceDataProgramRequest()
{
CustomerAccountId = Convert.ToInt64(customerAccountId)
};
svc.Open();
var response = new GetServiceDataProgramResponse();
var metaData = new RequestMetadata()
{
ClientIPAddress = "xx.xx.xx.xx",
TrackingNumber = "1",
UserID = "1"
};
svc.GetAccountData(metaData, request, out response);
}
I am getting an error stating I am passing anonymous credentials with the request.
With the updated code, now I receive a different exception:
UPDATE:
After making suggested changes as well as removing the service call from the using block (per this article), I'm now getting a MessageSecurityException:
Error message:
-$exception {"The HTTP request is unauthorized with client authentication scheme 'Anonymous'.
The authentication header received from the server was 'Basic realm=\"Spring Security Application\"'."}
System.Exception {System.ServiceModel.Security.MessageSecurityException}
Server stack trace:
at System.ServiceModel.Channels.HttpChannelUtilities.ValidateAuthentication(HttpWebRequest request, HttpWebResponse response, WebException responseException, HttpChannelFactory factory)
at System.ServiceModel.Channels.HttpChannelUtilities.ValidateRequestReplyResponse(HttpWebRequest request, HttpWebResponse response, HttpChannelFactory factory, WebException responseException)
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.DoOperation(SecuritySessionOperation operation, EndpointAddress target, Uri via, SecurityToken currentToken, TimeSpan timeout)
at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.GetTokenCore(TimeSpan timeout)
at System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(TimeSpan timeout)
at System.ServiceModel.Security.SecuritySessionClientSettings`1.ClientSecuritySessionChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.CallOpenOnce.System.ServiceModel.Channels.ServiceChannel.ICallOnce.Call(ServiceChannel channel, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(TimeSpan timeout, CallOnceManager cascade)
at System.ServiceModel.Channels.ServiceChannel.EnsureOpened(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)\r\n\r\nException rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at [ServiceName].GetAccountData(svcRequest request)
at [ServiceName].GetAccountData(GetAccountDataRequest request)
in c:\\[Project]\\service references\\[ServiceName]\\reference.cs:line 3480
at c:\\[Project]\\service references\\[ServiceName](RequestMetadata RequestMetadata, ServiceRequest, ServiceResponse& ServiceResponse)
in c:\\[Project]\\service references\\[ServiceName]\\reference.cs:line 3487
at c:\\[Project]\\service references\\[ServiceName].CheckAccountForPaymentPlan(String customerAccountId)
in c:\\[Project]\\service references\\[ServiceName]\\\\PlanCheckService.cs:line 32
With TransportWithMessageCredential, you're specifying that you'll use message security to secure the individual messages, which requires the client credentials (username and password). You need something similar to the config from the msdn link as below:
<wsHttpBinding>
<binding name="WsHttpBinding_ICalculator">
<security mode="TransportWithMessageCredential" >
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
This doesn't require specifying a certificate for client credentials. Transport security (using https and a valid ssl certificate) works in the same way a web site does, it doesn't require additional credentials/certs from the user. Valid certs from trusted certificate authorities are installed on the server (client machines are able to validate them) and the handshake process secures the channel. This does not require you to set clientCrdentials in config. You just need to install a valid cert (or test cert for dev) and configure the server config to point to it with something similar to:
<behaviors>
<serviceBehaviors>
<behavior name="mySvcBehavior">
<serviceCredentials>
<serviceCertificate findValue="contoso.com"
x509FindType="FindByIssuerName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
Try to remove the < transport clientCredentialType="Certificate" /> from your server config as a starter, update service refs and ensure your cert is working and configured correctly. Post your actual exceptions and more config if you still have problems.
For a good WCF source try: CodePlex, it helped me out no end when I started with WCF. The different application scenarios provide useful checklists to help ensure you don't miss any steps in your configuration.
Good Luck
UPDATE:
Once a channel is faulted, it needs to be recreated as you won't be able to communicate with the service until it's been reset. So add a check to recreate it:
If svc.State = CommunicationState.Faulted Then....
Try remove the svc.Open() line as I've never actually used that. I checked msdn for usage details but got about 2 lines of useless info. Once the service is setup you should be able to communicate with it without having to open it specifically. Not sure that this will actually make a difference though?!
Other things to check:
- can you right click on the service and view in browser without problems?
- in IIS can you view the certificate in directory security without any issues?
- debug up to the point before the service call is made and check the credentials are correctly assigned.
- check server event viewer for any info it may have logged with the request (if it's getting that far).
Also, here's some of the exception I trap to determine issues using ex.GetBaseException.GetType:
ServiceModel.EndpointNotFoundException
Server connection problem - either IIS isn't running or invalid server name
ServiceModel.Security.MessageSecurityException
Base Exception - ServiceModel.FaultException
"FailedAuthentication" - Bad credentials entered by user
"InvalidSecurity" - DB Error - either account has no access to DB, DB name in web config is incorrect, user password has expired in the database
"InvalidRequest" - Certificate accessibility issue - check service account has access to certificate
Base Exception - Net.WebException
Unauthorized access 401 - Check anonymous access in IIS is turned on
Base Exception - IdentityModel.Tokens.SecurityTokenValidationException
No service certificate is assigned in IIS
Base Exception - System.ServiceModel.CommunicationException
Identity mismatch with server certificate - eg in dev environment, cert named "localhost" so if you enter PC number or IP address for service you'll see this