29

I am trying to call a webservice using WCF that uses the following encoding:

<?xml version="1.0" encoding="ISO-8859-1" ?> 

I cannot change the encoding for this webservice. I have generated a wcf proxy and when I try and call the proxy, I get the following error:

FailedSystem.ServiceModel.ProtocolException: The content type text/xml; charset=ISO-8859-1 of the response message does not match the content type of the binding (text/xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly.

Has anyone any idea how I call a webservice that is not UTF-8 using wcf?

user229044
  • 232,980
  • 40
  • 330
  • 338
dagda1
  • 26,856
  • 59
  • 237
  • 450

5 Answers5

24

You cannot do this out of the box in WCF - unfortunately.

I faced the same issue, and came up with a custom binding (either in config or in code) that you can use. It's based on HTTP transport, and if that's of help to you, I could post the custom config here, or send you a link to the C# code for the binding.

This MSDN page here shows how to create a "CustomTextEncoder" which can support more than utf-8, utf-16 and unicode encodings. It includes full sample source code and was very useful for me to get things going.

This blog post here shows some additional things to take into account when trying to get that custom text encoder to work properly.

Hope that helps.

Marc

UPDATE:
Based on the Microsoft sample I mentioned above for a CustomTextEncoder, you can easily create a custom binding with a ISO-8859-1 text message encoding - just use this snippet of config (assuming you have downloaded and compiled and referenced that Microsoft sample):

  <system.serviceModel>
    <extensions>
      <bindingElementExtensions>
        <add name="customTextMessageEncoding"
             type="Microsoft.ServiceModel.Samples.CustomTextMessageEncodingElement, 
                   Microsoft.ServiceModel.Samples.CustomTextEncoder"/>
      </bindingElementExtensions>
    </extensions>
    <bindings>
      <customBinding>
        <binding name="ISO8859Binding" >
          <customTextMessageEncoding encoding="ISO-8859-1" />
          <httpTransport />
        </binding>
      </customBinding>
    </bindings>

    <client>
      <endpoint name="Test"
                address="......."
                binding="customBinding"
                bindingConfiguration="ISO8859Binding" 
                contract="IYourContract" />
    </client>
  </system.serviceModel>

You can find that code, plus a ISO88591Binding that basically wraps this whole config into code, on my Skydrive directory WCF ISO 8859-1 Encoding. Watch out, though - this is based on my requirements I had at the time, e.g. my service I needed to talk to required https, and also some other a bit whacky settings, so you might need to tweak those or make them configurable in config, if needed. That ISO88591Binding" project also contains a netHttpBinding that again is a Microsoft-provided sample which I used to get the hang of writing my own custom binding in code.

Writing a custom binding in WCF is definitely possible - but not really for the faint of heart....

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • If you could post a link to the config and the code, that would be great. I think this is a serious failing in WCF. – dagda1 Dec 16 '09 at 19:40
  • not really - the whole Internet and SOA world really is based on Unicode standards. In my opinion, even having to deal with ISO-8859-1 is a failing, more than WCF not supporting it out of the box... – marc_s Dec 16 '09 at 20:03
  • 1
    I use CustomTextMessageEncodingElement, but I get error: The content type text/xml; charset=ISO-8859-1 of the response message does not match the content type of the binding (text/xml;charset=iso-8859-1). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first 1024 bytes of the response were: – Kiquenet Nov 10 '10 at 10:58
  • From code of Samples Msdn, I modify constructor from CustomTextMessageEncoder class. replace "{0};charset={1}" by "{0}; charset={1}" (I have included an blank). Then, I get error:The message version of the outgoing message (Soap11 (http://schemas.xmlsoap.org/soap/envelope/) AddressingNone (http://schemas.microsoft.com/ws/2005/05/addressing/none)) does not match that of the encoder (Soap12 (http://www.w3.org/2003/05/soap-envelope) Addressing10 (http://www.w3.org/2005/08/addressing)). Make sure the binding is configured with the same version as the message. – Kiquenet Nov 10 '10 at 11:30
  • In your sample (Skydrive directory), where is CustomISOBinding.WsgHttpsBindingCollectionElement class ?? It doesn't appears in solution. – Kiquenet Nov 10 '10 at 11:51
  • 1
    @alhambraeidos: there's a file `ISO88591HttpsBindingCollectionElement.cs` in my solution - that should be the file; sample.config is invalid - that line should be: `` - sorry about that. – marc_s Nov 10 '10 at 14:27
  • 1
    Marc, is it possible, and if the answer is "yes" - how, to add the authorization on the http level in your custom binding? I have looked into your solution and could not find such a thing (or I didn't looked in the right place). Thank you. – Belurd Dec 03 '15 at 14:19
  • Skydrive is empty. – galmok Jan 29 '20 at 08:01
9

Another option is using the older .NET Framework 2.0 ASMX WebServices technology which supports iso-8859-1 out of the box:

enter image description here

enter image description here

And if the service uses Basic HTTP Authentication you can specify it like this:

TheService theService = new TheService();
theService.Credentials = new NetworkCredential("username", "password");
Saeb Amini
  • 23,054
  • 9
  • 78
  • 76
  • 1
    This worked for me for connecting my C# client to php soap server – mehrdad seyrafi Sep 17 '16 at 16:09
  • 1
    This procedure requires less effort, a LOT less, it's also possible to configure everything in the code without needing to edit app.config or web.config, so if you have this code in a library this is easier than other options. It can also be configured with app/web.config if desired. – fbiazi Aug 11 '22 at 13:03
7

In this link you can download the files for make the custom binding, and do the following in your code:

CustomBinding binding = new CustomBinding(
   new CustomTextMessageBindingElement("iso-8859-1", "text/xml", MessageVersion.Soap11),
   new HttpTransportBindingElement());

myWebService client = new myWebService();

client.Endpoint.Binding = binding;
Juan Carlos Velez
  • 2,840
  • 2
  • 34
  • 48
3

If you don't want to deal with tons of downloaded codes and low level implementation you can workaround by using a old style request using HttpWebRequest class, as described here. Now, you will exempt of automatized code and facilities of the Interface and will play with manual Xml parsing.

rkawano
  • 2,443
  • 22
  • 22
1

I know it is an old post but I faced the same issue recently. And some response links are no longer available.

For me, the problem was caused because the server responded with ISO-8859-1 but my Client accepts UTF-8 by default.

The solution was to create a CustomTextMessageBindingElement that matches with the server configs:

public class CustomTextMessageBindingElement : MessageEncodingBindingElement
{
    public override MessageVersion MessageVersion { get; set; }
    public string MediaType { get; set; }
    public string Encoding { get; set; }

    CustomTextMessageBindingElement(CustomTextMessageBindingElement binding)
        : this(binding.Encoding, binding.MediaType, binding.MessageVersion)
    {
    }

    public CustomTextMessageBindingElement(string encoding, string mediaType,
   MessageVersion messageVersion)
    {
        this.MessageVersion = messageVersion;
        this.MediaType = mediaType;
        this.Encoding = encoding;
    }

    public CustomTextMessageBindingElement(string encoding, MessageVersion messageVersion)
    {
        this.Encoding = encoding;
        this.MessageVersion = messageVersion;
        if (messageVersion.Envelope == EnvelopeVersion.Soap11)
        {
            this.MediaType = "text/xml";
        }
        else if (messageVersion.Envelope == EnvelopeVersion.Soap12)
        {
            this.MediaType = "application/soap+xml";
        }
        else
        {
            this.MediaType = "application/xml";
        }
    }

    public override BindingElement Clone()
    {
        return new CustomTextMessageBindingElement(this);
    }

    public override MessageEncoderFactory CreateMessageEncoderFactory()
    {
        return new CustomTextMessageEncoderFactory(MediaType, Encoding, MessageVersion);
    }

    public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        context.BindingParameters.Add(this);
        return context.BuildInnerChannelFactory<TChannel>();
    }
}
public class CustomTextMessageEncoder : MessageEncoder
{
    private CustomTextMessageEncoderFactory factory;
    private XmlWriterSettings writerSettings;
    private string contentType;

    public CustomTextMessageEncoder(CustomTextMessageEncoderFactory factory)
    {
        this.factory = factory;

        this.writerSettings = new XmlWriterSettings();
        this.writerSettings.Encoding = Encoding.GetEncoding(factory.CharSet);
        this.contentType = string.Format("{0}; charset={1}",
            this.factory.MediaType, this.writerSettings.Encoding.HeaderName);
    }

    public override bool IsContentTypeSupported(string contentType)
    {
        return true;
    }

    public override string ContentType
    {
        get
        {
            return this.contentType;
        }
    }

    public override string MediaType
    {
        get
        {
            return factory.MediaType;
        }
    }

    public override MessageVersion MessageVersion
    {
        get
        {
            return this.factory.MessageVersion;
        }
    }

    public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
    {
        byte[] msgContents = new byte[buffer.Count];
        Array.Copy(buffer.Array, buffer.Offset, msgContents, 0, msgContents.Length);
        bufferManager.ReturnBuffer(buffer.Array);

        MemoryStream stream = new MemoryStream(msgContents);
        return ReadMessage(stream, int.MaxValue);
    }

    public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)
    {
        XmlReader reader = XmlReader.Create(stream);
        return Message.CreateMessage(reader, maxSizeOfHeaders, this.MessageVersion);
    }

    public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
    {
        MemoryStream stream = new MemoryStream();
        XmlWriter writer = XmlWriter.Create(stream, this.writerSettings);
        message.WriteMessage(writer);
        writer.Close();

        byte[] messageBytes = stream.GetBuffer();
        int messageLength = (int)stream.Position;
        stream.Close();

        int totalLength = messageLength + messageOffset;
        byte[] totalBytes = bufferManager.TakeBuffer(totalLength);
        Array.Copy(messageBytes, 0, totalBytes, messageOffset, messageLength);

        ArraySegment<byte> byteArray = new ArraySegment<byte>(totalBytes, messageOffset, messageLength);
        return byteArray;
    }

    public override void WriteMessage(Message message, Stream stream)
    {
        XmlWriter writer = XmlWriter.Create(stream, this.writerSettings);
        message.WriteMessage(writer);
        writer.Close();
    }
}
public class CustomTextMessageEncoderFactory : MessageEncoderFactory
{
    private MessageEncoder encoder;
    private MessageVersion version;
    private string mediaType;
    private string charSet;

    internal CustomTextMessageEncoderFactory(string mediaType, string charSet,
        MessageVersion version)
    {
        this.version = version;
        this.mediaType = mediaType;
        this.charSet = charSet;
        this.encoder = new CustomTextMessageEncoder(this);
    }

    public override MessageEncoder Encoder
    {
        get
        {
            return this.encoder;
        }
    }

    public override MessageVersion MessageVersion
    {
        get
        {
            return this.version;
        }
    }

    internal string MediaType
    {
        get
        {
            return this.mediaType;
        }
    }

    internal string CharSet
    {
        get
        {
            return this.charSet;
        }
    }
}

And then create a CustomBinding that use the CustomTextMessageBindingElement:

private Binding GetBindConfiguration()
{
    //This configs could be different in Soap server that you are implementing
    //set the soap server config: encoding, mediaType and Soap Version 1.1
    var custom = new CustomTextMessageBindingElement("ISO-8859-1", "text/xml", MessageVersion.CreateVersion(EnvelopeVersion.Soap11, AddressingVersion.None));
    var httpsBindingElement = new HttpsTransportBindingElement()
    {
        MaxReceivedMessageSize = int.MaxValue,
    };

    return new CustomBinding(custom, httpsBindingElement);
}

Finaly use the CustomBinding:

//Auto generated class using svcutil tool
var clientSoap = new WebService_SigISSPortTypeClient(GetBindConfiguration(), new EndpointAddress(new Uri("https://serverAddress.com.br")));

I believe that this solution, should work for .net core too with little changes and using the new dotnet-svcutil tool

References:
Implementation of CustomTextMessageEncodingBindingElement
Docs for Message Encoder Extensibility

fsbflavio
  • 664
  • 10
  • 22