I have created a Windows WCF service and generated WSDL for it. I am using basic http binding. When I try to call this from IOS (using the generated basicHttpBinding wsdl2objc class) I get an error when calling one of the WCF method. The error (from the logXMLInOut) states the problem is due to an address mismatch, however I have no clue about how to debug this and\or what I am doing wrong.
Here is the full error message:
2011-06-10 11:54:05.534 IpadWebServiceTest[10149:207] OutputHeaders:
{
"Content-Length" = 462;
"Content-Type" = "application/soap+xml; charset=utf-8";
Host = "192.168.1.69";
Soapaction = "FredIannon.TestService/IHelloIndigoService/FredContractNoParm";
"User-Agent" = wsdl2objc;
}
2011-06-10 11:54:05.536 IpadWebServiceTest[10149:207] OutputBody:
<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:HelloIndigoService="FredIannon.TestService" xmlns:tns1="http://schemas.microsoft.com/2003/10/Serialization/" xsl:version="1.0">
<soap:Body>
<HelloIndigoService:FredContractNoParm/>
</soap:Body>
</soap:Envelope>
2011-06-10 11:54:05.589 IpadWebServiceTest[10149:207] ResponseStatus: 500
2011-06-10 11:54:05.590 IpadWebServiceTest[10149:207] ResponseHeaders:
{
"Content-Length" = 615;
"Content-Type" = "application/soap+xml; charset=utf-8";
Date = "Fri, 10 Jun 2011 16:54:05 GMT";
Server = "Microsoft-HTTPAPI/2.0";
}
2011-06-10 11:54:05.591 IpadWebServiceTest[10149:207] ResponseBody:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><a:Action s:mustUnderstand="1">http://www.w3.org/2005/08/addressing/soap/fault</a:Action></s:Header><s:Body><s:Fault><s:Code><s:Value>s:Sender</s:Value><s:Subcode><s:Value>a:DestinationUnreachable</s:Value></s:Subcode></s:Code><s:Reason><s:Text xml:lang="en-US">The message with To '' cannot be processed at the receiver, due to an AddressFilter mismatch at the EndpointDispatcher. Check that the sender and receiver's EndpointAddresses agree.</s:Text></s:Reason></s:Fault></s:Body></s:Envelope>
Here is the hosts app.config
<services>
<service
behaviorConfiguration="serviceBehavior"
name="HelloIndigo.HelloIndigoService">
<endpoint address="HelloIndigoService"
binding="basicHttpBinding"
bindingNamespace="FredIannon.TestService"
name="basicHttp"
contract="HelloIndigo.IHelloIndigoService" />
<endpoint binding="mexHttpBinding"
name="mex"
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://192.168.1.69:8000/HelloIndigo/HelloIndigoService" />
<!-- <add baseAddress="http://localhost:8000/HelloIndigo/HelloIndigoService" /> -->
</baseAddresses>
</host>
</service>
</services>
The WCF service is receiving your soap but it can't correctly parse the soap headers generated by the wsdl2objc proxy. This post has a good description of the actual problem you are encountering. To determine what are the required headers, start by creating a .NET WCF client that successfully invokes the service. See what the expected headers should be by capturing the soap it sends. Modify your proxy code to produce the exact soap headers as the .NET client.
Related
I am logging outgoing WCF Soap requests from my application, and discovered a discrepancy between what I'm logging and what actually goes out to the server via the wire.
Here is what I'm logging:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/myinterface</Action>
</s:Header>
<s:Body>
<myinterface xmlns="http://tempuri.org/">
...
Here is what actually goes out (captured with WireShark):
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<myinterface xmlns="http://tempuri.org/">
...
You'll notice that the WireShark capture does not have the Header or Action elements.
I only noticed this because I tried pushing my logged request out through SoapUI.
I'm using this code to log the request:
public class InspectorBehavior : IEndpointBehavior
{
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new WCFMessageInspector());
}
And:
public class WCFMessageInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)
{
Log(request.ToString());
return null;
}
And:
client.Endpoint.Address = new System.ServiceModel.EndpointAddress(address);
client.Endpoint.Behaviors.Add(new InspectorBehavior());
Does anyone know if there's a way to capture exactly what is going out (verbatim), without any post-processing as shown above?
You can configure WCF Message Logging in your App.config to log messages at transport level.
This should capture the message as late as possible:
For outgoing messages, logging happens immediately after the message leaves user code and immediately before the message goes on the wire.
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="messages"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="c:\logs\messages.svclog" />
</listeners>
</source>
</sources>
</system.diagnostics>
<system.serviceModel>
<diagnostics>
<messageLogging
logEntireMessage="true"
logMalformedMessages="true"
logMessagesAtServiceLevel="false"
logMessagesAtTransportLevel="true"
maxMessagesToLog="1000"
maxSizeOfMessageToLog="1000000"/>
</diagnostics>
</system.serviceModel>
You can capture exact content via custom MessageEncoder, unlike message inspectors (IDispatchMessageInspector / IClientMessageInspector) it sees original byte content including any malformed XML data.
However it's a bit harder to implement this approach. You have to wrap a standard textMessageEncoding as custom binding element and adjust config file to use that custom binding.
Also you can see as example how I did it in my project - wrapping textMessageEncoding, logging encoder, custom binding element and config.
I have a web service method which can return below response without (SOAPAction: "")
Method
public string SyncData(Stream xml)
{
}
web.config
<service behaviorConfiguration="" name="PRO.API">
<endpoint address="" behaviorConfiguration="web" binding="customBinding"
bindingConfiguration="RawReceiveCapable" contract="PRO.IAPI" />
</service>
customBinding
<customBinding>
<binding name="RawReceiveCapable">
<webMessageEncoding webContentTypeMapperType="PRO.RawContentTypeMapper, PRO, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<httpTransport manualAddressing="true" maxReceivedMessageSize="524288000" transferMode="Streamed" />
</binding>
</customBinding>
public class RawContentTypeMapper : WebContentTypeMapper
{
public override WebContentFormat GetMessageFormatForContentType(string contentType)
{
if (contentType.Contains("text/xml")
|| contentType.Contains("application/xml")
|| contentType.Contains("text/html"))
{
return WebContentFormat.Raw;
}
else
{
return WebContentFormat.Default;
}
}
}
Required Response with SOAPAction: ""
Content-Type: text/xml;charset=utf-8
Content-Length: 346
SOAPAction: ""
<soapenv:Envelope" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<syncOrderRelationResponse xmlns="http://www.csapi.org/schema/parlayx/data/sync/v1_0/local">
<result>0</result>
<resultDescription>success</resultDescription>
</syncOrderRelationResponse>
</soapenv:Body>
</soapenv:Envelope>
What should I do to get response with SOAPAction, thanks in adavnce?
please see this link for "Sample SOAP response"
http://support.sas.com/documentation/cdl/en/wbsvcdg/62759/HTML/default/viewer.htm#n1wblekhip1yrln1fv2s5b6a2d9f.htm
You can specify Action property in OperationContract attribute. Check this question How to specify custom SoapAction for WCF
I'm newbie in WCF. I developed a WCF service and client for it. The service methods will retrieve custom data which uses custom XML serializer. I've read, in this case, contract methods should be marked with [XmlSerializerFormat]:
[ServiceContract]
[XmlSerializerFormat]
public interface ITSService
{
[OperationContract]
[XmlSerializerFormat]
ProtocolDocument GetReferenceData(string referenceType, SerializableDictionary<string, string> args);
ProtocolDocument implements IXmlSerializable:
[XmlRoot("protocol", Namespace = Protocol30Namespace)]
[Type(Name = "protocol", Namespace = Protocol30Namespace)]
public class ProtocolDocument : ProtocolElement, ICloneable, IXmlSerializable
VS 2010 chooses wsHttpBinding by default. I don't need in security, so I turned it off.
Here is the service configuration:
<services>
<service name="MyNamespace.TSService"
behaviorConfiguration="MyNamespace.TSServiceBehavior">
<host>
<baseAddresses>
<add baseAddress = "http://localhost:51944/TSService.svc" />
</baseAddresses>
</host>
<endpoint
address=""
binding="wsHttpBinding"
bindingConfiguration="nonSecurityWSHttpBinding"
contract="MyNamespace.ITSService">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint
address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyNamespace.TSServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="nonSecurityWSHttpBinding">
<security mode="None">
<transport clientCredentialType="None"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
Then I generated the client for this service, but the result could not be deserialized. Fiddler says SOAP wrapped serialized data into GetReferenceDataResult and GetReferenceDataResponse:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/ITourSystemService/GetReferenceDataResponse</a:Action>
<a:RelatesTo>urn:uuid:3d7f6dc0-4961-4bc5-b1fc-c9997af9fbd4</a:RelatesTo>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GetReferenceDataResponse xmlns="http://tempuri.org/">
<GetReferenceDataResult>
<header version="3.0" language="Russian"/>
<references/>
</GetReferenceDataResult>
</GetReferenceDataResponse>
</s:Body>
</s:Envelope>
But the root element is missing! What should I do?
PS Serialization impl:
void IXmlSerializable.ReadXml(XmlReader reader)
{
var serializer = new ProtocolDocumentXmlSerializer();
serializer.Deserialize(this, reader);
}
void IXmlSerializable.WriteXml(XmlWriter writer)
{
//
// Serialize everything except the root element, because it was already written by .NET XML-serialization mechanism
var xmlSerializationFlags = XmlSerializationFlags.All & ~XmlSerializationFlags.IncludeRootElement;
var serializer = new ProtocolDocumentXmlSerializer();
serializer.Serialize(this, writer, xmlSerializationFlags);
}
Xml-serialization works well. It is already in use. I suppose smth wrong with my WCF-configuration.
You appear to have explicitly told it to ignore your root element in your WriteXml method - that's would be why there is no root element
var xmlSerializationFlags = XmlSerializationFlags.All & ~XmlSerializationFlags.IncludeRootElement;
If you cannot get deserialization to work you can always work at the XML level by using Message as the return type in your client's contract and then call GetReaderAtBody on the message, load the data into XElement and use LINQ to XML to transform the XML into objects to work with at the client side
I'm getting this
HTTP Error 404.0 - Not Found
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
when trying to access the service from my browser. Here is my config.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<!-- Note: the service name must match the configuration name for the service implementation. -->
<service name="WcfServiceLibrary.Service1" behaviorConfiguration="MyServiceTypeBehaviors" >
<!-- Add the following endpoint. -->
<!-- Note: your service must have an http base address to add this endpoint. -->
<endpoint contract="WcfServiceLibrary.Service1" binding="basicHttpBinding" address="http://localhost/service1" />
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="http://localhost/service1/mex" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceTypeBehaviors" >
<!-- Add the following element to your service behavior configuration. -->
<serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost/service1" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
When I type http://localhost/service1 in the web browser I get the 404. But if I remove the app.config below and just simpley do this in the code behind
string serviceUrl = "http://localhost/service1";
Uri uri = new Uri(serviceUrl);
host = new ServiceHost(typeof(Service1), uri);
host.Open();
All works well... Any ideas? Seems simple enough.
I think you are missing the host element under your services:
<service name="WcfServiceLibrary2.Service1">
<host>
<baseAddresses>
<add baseAddress = "http://localhost/service1" />
</baseAddresses>
</host>
<endpoint address ="" binding="wsHttpBinding" contract="WcfServiceLibrary2.IService1">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
Service host does not need URL then.
static void Main(string[] args)
{
var host = new ServiceHost(typeof(Service1));
host.Open();
Console.WriteLine("Host running");
Console.ReadLine();
}
You can show http://localhost/service1?Wsdl in the browser but mex only works with add service reference or WCFTestClient (C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE) because you will get the HTTP Bad Request error which 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.
I have a self hosted WCF 4 service, catering the same contract via basicHttpBinding for Silverlight 4 clients and wsHttpBinding for the others. The code is very short and simple and provided here.
I get the following error when trying to access the a service method from WCF:
Message=An error occurred while trying to make a request to URI
http://localhost:8008/WCF4Silverlight.MyService/SL. This could be
due to attempting to access a service in a cross-domain way without a
proper cross-domain policy in place, or a policy that is unsuitable
for SOAP services. You may need to contact the owner of the service to
publish a cross-domain policy file and to ensure it allows
SOAP-related HTTP headers to be sent. This error may also be caused by
using internal types in the web service proxy without using the
InternalsVisibleToAttribute attribute. Please see the inner exception
for more details.
I do have the method, GetClientAccessPolicy() serving the cross-domain policy using WebGet attribute, and I am kind of sure that there is a problem with it getting exposed properly. Your insight into the problem will be highly appreciated. If I type http://localhost:8008/WCF4Silverlight.MyService/clientaccesspolicy.xml in the browser, I do get the xml for the same, but the call from Silverlight always fails with the above error.
Here is the code for the WCF service:
namespace WCF4Silverlight
{
[ServiceContract(SessionMode = SessionMode.NotAllowed)]
public interface IClientAccessPolicy
{
[OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")]
Stream GetClientAccessPolicy();
}
}
namespace WCF4Silverlight
{
public class MyService: IMyService, IClientAccessPolicy
{
public Stream GetClientAccessPolicy()
{
const string result = #"<?xml version=""1.0"" encoding=""utf-8""?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers=""*"">
<domain uri=""*""/>
</allow-from>
<grant-to>
<resource path=""/"" include-subpaths=""true""/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>";
if (WebOperationContext.Current != null)
WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml"; return new MemoryStream(Encoding.UTF8.GetBytes(result));
}
}
//Other service methods....
}
Here is code that publishes the service:
class Program
{
static void Main(string[] args)
{
ServiceHost myServiceHost = new ServiceHost(typeof(MyService));
myServiceHost.Open();
//Wait for client action.
myServiceHost.Close();
}
}
Here is the app.config for the WCF service host:
<service name="WCF4Silverlight.MyService" behaviorConfiguration="MyServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8008/MyService/"/>
</baseAddresses>
</host>
<endpoint address="general" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IMyService" contract="WCF4Silverlight.IMyService"/>
<endpoint address="SL" binding="basicHttpBinding" bindingConfiguration="basicHttpBinding_IMyService" contract="WCF4Silverlight.IMyService"/>
<endpoint address="" binding="webHttpBinding" bindingConfiguration="webHttpBinding_IMyService" behaviorConfiguration="webHttpBehavior" contract="WCF4Silverlight.IClientAccessPolicy" />
</service>
And here is the ServiceReferences.ClientConfig for the Silverlight client:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IMyService" maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="None" />
</binding>
</basicHttpBinding>
<customBinding>
<binding name="WSHttpBinding_IMyService">
<textMessageEncoding messageVersion="Default" writeEncoding="utf-8" />
<httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="http://localhost:8008/MyService/SL"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IMyService"
contract="myWCFService.IMyService" name="BasicHttpBinding_IMyService" />
</client>
</system.serviceModel>
This is what I did to resolve the issue:
1) Used Fiddler to see where the WCF calls were directed. Fiddler told that the calls were failing to HOST - http:/localhost:8008 and URL - /clientaccesspolicy.xml.
2) Created a different class ClientAccessPolicy implementing IClientAccessPolicy (with WebGet for /clientaccesspolicy.xml).
3) Added another section in app.config of the host for a new service hosting the Clientaccesspolicy class. This one had its base address as http:/localhost:8008/
<service name="WCF4Silverlight.ClientAccessPolicy" behaviorConfiguration="ClientAccessPolicyBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8008/"/>
</baseAddresses>
</host>
<endpoint address="" binding="webHttpBinding" bindingConfiguration="webHttpBinding_IMyService" behaviorConfiguration="webHttpBehavior" contract="WCF4Silverlight.IClientAccessPolicy" />
</service>
4) In the hosting code, created another instance of ServiceHost and launched the new service with Clientaccesspolicy
ServiceHost clientAccessPolicyHost = new ServiceHost(typeof(ClientAccessPolicy)); clientAccessPolicyHost.Open();
5) In the Silverlight client, deleted the existing reference to the WCF and added the one to the newly hosted service.
The WCF calls from Silverlight are now going through.
Self hosted services on machines without IIS where the clientaccesspolicy can't be served up from the root, can instead use this method to dynamically serve up the policy on Port 80:
http://blogs.msdn.com/b/carlosfigueira/archive/2010/07/25/enabling-cross-domain-calls-for-sl-apps-on-self-hosted-tcp-services.aspx
The easiest way to debug this kind of issues is by using Fiddler (www.fiddler2.com) to intercept the HTTP traffic. You'll immediately see if clientAccessPolicy.xml is requested, where it is expected to be, and what is the result.
If you get a 404 (resource not found) the file is not at the expected location (but your webGet annotation looks good to me), otherwise the issue is within the xml itself.
This is a very permissive clientAccessPolicy.xml that I usually use for development/testing purposes:
<?xml version="1.0" ?>
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>
If you're using a self-hosted web service, you need to throw your ClientAccessPolicy.xml into the root of a website that can be reached on port 80 of your machine (e.g., http://localhost:80/ClientAccessPolicy.xml). This was new in Silverlight 4, and unfortunately, I haven't found it clearly explained in the MS docs. (It's mentioned, but it's not terribly clear.)