简体   繁体   English

从 WCF 调用使用 ISO-8859-1 编码的网络服务

[英]Calling a webservice that uses ISO-8859-1 encoding from WCF

I am trying to call a webservice using WCF that uses the following encoding:我正在尝试使用 WCF 调用使用以下编码的网络服务:

<?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:我生成了一个 wcf 代理,当我尝试调用该代理时,出现以下错误:

FailedSystem.ServiceModel.ProtocolException: The content type text/xml; FailedSystem.ServiceModel.ProtocolException:内容类型为text/xml; charset=ISO-8859-1 of the response message does not match the content type of the binding (text/xml; charset=utf-8).响应消息的 charset=ISO-8859-1 与绑定的内容类型不匹配(text/xml;charset=utf-8)。 If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly.如果使用自定义编码器,请确保正确实施 IsContentTypeSupported 方法。

Has anyone any idea how I call a webservice that is not UTF-8 using wcf?有谁知道我如何使用 wcf 调用不是 UTF-8 的网络服务?

You cannot do this out of the box in WCF - unfortunately. 你不能在WCF中开箱即用 - 不幸的是。

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. 它基于HTTP传输,如果对您有所帮助,我可以在此处发布自定义配置,或者向您发送绑定的C#代码链接。

This MSDN page here shows how to create a "CustomTextEncoder" which can support more than utf-8, utf-16 and unicode encodings. 这里的MSDN页面显示了如何创建一个“CustomTextEncoder”,它可以支持超过utf-8,utf-16和unicode编码。 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): 根据我上面提到的用于CustomTextEncoder的Microsoft示例,您可以轻松地使用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>

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 . 您可以在我的Skydrive目录WCF ISO 8859-1编码上找到该代码,以及基本上将整个配置包装到代码中的ISO88591Binding Watch out, though - this is based on my requirements I had at the time, eg 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. 但请注意 - 这是基于我当时的要求,例如我的服务我需要与所需的https进行通信,还有其他一些有点糟糕的设置,因此您可能需要调整它们或使它们在配置中可配置, 如果需要的话。 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. ISO88591Binding“项目还包含一个netHttpBinding,它再次是Microsoft提供的示例,我曾经在代码中编写自己的自定义绑定。

Writing a custom binding in WCF is definitely possible - but not really for the faint of heart.... 在WCF中编写自定义绑定绝对是可能的 - 但对于胆小的人来说却并非如此......

Another option is using the older .NET Framework 2.0 ASMX WebServices technology which supports iso-8859-1 out of the box: 另一种选择是使用较旧的.NET Framework 2.0 ASMX WebServices技术,该技术支持开箱即用的iso-8859-1

在此输入图像描述

在此输入图像描述

And if the service uses Basic HTTP Authentication you can specify it like this: 如果服务使用基本HTTP身份验证,您可以像这样指定它:

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

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;

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 . 如果您不想处理大量下载的代码和低级实现,可以使用HttpWebRequest类使用旧样式请求来解决此问题 ,如此处所述。 Now, you will exempt of automatized code and facilities of the Interface and will play with manual Xml parsing. 现在,您将免除接口的自动化代码和工具,并将使用手动Xml解析。

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.对我来说,问题是因为服务器响应 ISO-8859-1 但我的客户端默认接受 UTF-8。

The solution was to create a CustomTextMessageBindingElement that matches with the server configs :解决方案是创建一个与服务器配置匹配的 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;
        }
    }
}

And then create a CustomBinding that use the CustomTextMessageBindingElement:然后创建一个使用 CustomTextMessageBindingElement 的 CustomBinding:

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:最后使用 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 do.net-svcutil tool我相信这个解决方案应该也适用于 .net 核心,只需稍作改动并使用新的 do.net-svcutil 工具

References:参考:
Implementation of CustomTextMessageEncodingBindingElement CustomTextMessageEncodingBindingElement 的实现
Docs for Message Encoder Extensibility 消息编码器可扩展性文档

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM