Вызов веб-службы, использующей кодировку ISO-8859-1 из WCF

Я пытаюсь вызвать веб-сервис, используя WCF, который использует следующую кодировку:

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

Я не могу изменить кодировку для этого веб-сервиса. Я сгенерировал прокси wcf, и когда я пытаюсь вызвать прокси, я получаю следующую ошибку:

FailedSystem.ServiceModel.ProtocolException: тип содержимого text/xml; charset=ISO-8859-1 ответного сообщения не соответствует типу содержимого привязки (text/xml; charset=utf-8). При использовании пользовательского кодировщика убедитесь, что метод IsContentTypeSupported реализован правильно.

Кто-нибудь есть идеи, как я называю веб-сервис, который не UTF-8, используя wcf?

5 ответов

Решение

Вы не можете сделать это из коробки в WCF - к сожалению.

Я столкнулся с той же проблемой и придумал пользовательскую привязку (либо в конфигурации, либо в коде), которую вы можете использовать. Он основан на HTTP-транспорте, и если он вам поможет, я мог бы опубликовать здесь пользовательскую конфигурацию или отправить ссылку на код C# для привязки.

На этой странице MSDN показано, как создать "CustomTextEncoder", который может поддерживать более чем кодировки utf-8, utf-16 и unicode. Он включает в себя полный пример исходного кода и был очень полезен для меня.

Этот пост в блоге показывает некоторые дополнительные вещи, которые необходимо учитывать при попытке правильной работы этого пользовательского кодировщика текста.

Надеюсь, это поможет.

Марк

ОБНОВИТЬ:
Основываясь на примере Microsoft, который я упоминал выше для CustomTextEncoder, вы можете легко создать пользовательскую привязку с кодировкой текстового сообщения ISO-8859-1 - просто используйте этот фрагмент конфигурации (при условии, что вы загрузили и скомпилировали и ссылались на этот пример Microsoft):

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

Вы можете найти этот код, а также ISO88591Binding это в основном оборачивает всю эту конфигурацию в код в моей директории Skydrive Кодировка ISO 8859-1 WCF. Однако будьте осторожны - это основано на моих требованиях, которые у меня были в то время, например, на моем сервисе мне нужно было поговорить с требуемыми https, а также на некоторых других немного причудливых настройках, так что вам может понадобиться настроить их или настроить в конфигурации, если нужно. Этот проект ISO88591Binding также содержит netHttpBinding, который также является примером, предоставленным Microsoft, который я использовал, чтобы научиться писать свое собственное пользовательское связывание в коде.

Написание пользовательского связывания в WCF определенно возможно, но не для слабонервных....

Другой вариант - использование более старой технологии.NET Framework 2.0 ASMX WebServices, которая поддерживает iso-8859-1 из коробки:

И если служба использует базовую HTTP-аутентификацию, вы можете указать ее следующим образом:

TheService theService = new TheService();
theService.Credentials = new NetworkCredential("username", "password");

По этой ссылке вы можете скачать файлы для создания пользовательского связывания и сделать следующее в своем коде:

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

myWebService client = new myWebService();

client.Endpoint.Binding = binding;

Если вы не хотите иметь дело с тоннами загруженных кодов и низкоуровневой реализацией, вы можете обойти эту проблему, используя запрос старого стиля с использованием класса HttpWebRequest, как описано здесь. Теперь вы освобождаетесь от автоматизированного кода и возможностей интерфейса и поиграете с ручным анализом XML.

Я знаю, что это старый пост, но недавно я столкнулся с той же проблемой. И некоторые ссылки на ответы больше не доступны.

Для меня проблема была вызвана тем, что сервер ответил ISO-8859-1, но мой клиент по умолчанию принимает UTF-8.

Решение состояло в том, чтобы создать CustomTextMessageBindingElement, который соответствует конфигурациям сервера :

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

А затем создайте CustomBinding, использующий 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);
}

Наконец, используйте CustomBinding:

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

Я считаю, что это решение должно работать и для ядра .net с небольшими изменениями и с использованием нового инструмента dotnet-svcutil.

Ссылки:
Реализация документации CustomTextMessageEncodingBindingElement
для расширения кодировщика сообщений

Другие вопросы по тегам