代码之家  ›  专栏  ›  技术社区  ›  Redwood

配置.NET WCF UTF-8反序列化程序以修改/放弃非最短形式的字符,而不是引发异常?

  •  11
  • Redwood  · 技术社区  · 14 年前

    我们有一个通过WCF托管的SOAP web服务。

    我们从其中一个客户机接收数据时,偶尔会使用非最短形式对UTF-8进行编码(请参见 http://www.unicode.org/versions/corrigendum1.html 关于这个的一些信息)。

    修改客户端并不容易,因为这些非最短形式的字符不是由我们的代码编码的。

    相反,我们希望编辑WCF服务以丢弃这些字符,用其他占位符字符替换它们,甚至接受非最短形式的字符。这些选项中的任何一个对于我们的用例都是可以接受的,尽管前一个选项是首选的,因为它们减少了任何安全风险。

    查看堆栈跟踪:

    System.ServiceModel.Dispatcher.NetDispatcherFaultException: The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://www.mydomain.com/:mytype. The InnerException message was 'There was an error deserializing the object of type MyNamespace.MyType`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c461934e089]]. '????' contains invalid UTF8 bytes.'.  Please see InnerException for more details. ---> System.Runtime.Serialization.SerializationException: There was an error deserializing the object of type MyNamespace.MyType`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c461934e089]]. '????' contains invalid UTF8 bytes. ---> System.Xml.XmlException: '????' contains invalid UTF8 bytes. ---> System.Text.DecoderFallbackException: Unable to translate bytes [C0] at index 0 from specified code page to Unicode.
       at System.Text.DecoderExceptionFallbackBuffer.Throw(Byte[] bytesUnknown, Int32 index)
       at System.Text.DecoderExceptionFallbackBuffer.Fallback(Byte[] bytesUnknown, Int32 index)
       at System.Text.DecoderFallbackBuffer.InternalFallback(Byte[] bytes, Byte* pBytes, Char*& chars)
       at System.Text.UTF8Encoding.FallbackInvalidByteSequence(Byte*& pSrc, Int32 ch, DecoderFallbackBuffer fallback, Char*& pTarget)
       at System.Text.UTF8Encoding.GetChars(Byte* bytes, Int32 byteCount, Char* chars, Int32 charCount, DecoderNLS baseDecoder)
       at System.Text.UTF8Encoding.GetChars(Byte[] bytes, Int32 byteIndex, Int32 byteCount, Char[] chars, Int32 charIndex)
       at System.Xml.XmlConverter.ToChars(Byte[] buffer, Int32 offset, Int32 count, Char[] chars, Int32 charOffset)
       --- End of inner exception stack trace ---
       at System.Xml.XmlConverter.ToChars(Byte[] buffer, Int32 offset, Int32 count, Char[] chars, Int32 charOffset)
       at System.Xml.XmlBufferReader.GetChars(Int32 offset, Int32 length, Char[] chars)
       at System.Xml.ValueHandle.GetCharsText()
       at System.Xml.ValueHandle.GetString()
       at System.Xml.XmlBaseReader.get_Value()
       at System.Xml.XmlDictionaryReader.ReadContentAsString(Int32 maxStringContentLength)
       at System.Xml.XmlBaseReader.ReadContentAsString()
       at System.Xml.XmlBaseReader.ReadElementContentAsString()
       at System.Runtime.Serialization.XmlReaderDelegator.ReadElementContentAsString()
       at ReadOrbliteCompatibleArrayOfstringFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString , XmlDictionaryString , CollectionDataContract )
       at System.Runtime.Serialization.CollectionDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
       at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, DataContract& dataContract)
       at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
       at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName)
       at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName)
       --- End of inner exception stack trace ---
       at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName)
       at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameterPart(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)
       --- End of inner exception stack trace ---
       at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameterPart(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)
       at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameters(XmlDictionaryReader reader, PartInfo[] parts, Object[] parameters, Boolean isRequest)
       at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeBody(XmlDictionaryReader reader, MessageVersion version, String action, MessageDescription messageDescription, Object[] parameters, Boolean isRequest)
       at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeBodyContents(Message message, Object[] parameters, Boolean isRequest)
       at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeRequest(Message message, Object[] parameters)
       at System.ServiceModel.Dispatcher.DispatchOperationRuntime.DeserializeInputs(MessageRpc& rpc)
       at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
       at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
       at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
       at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
    

    看起来我们想要的是把解码器从 System.Text.DecoderExceptionFallback System.Text.DecoderReplacementFallback .

    有人能告诉我用替换回退覆盖默认异常回退的正确位置和方法吗?

    1 回复  |  直到 14 年前
        1
  •  10
  •   Redwood    14 年前

    一种可能的探索是创建一个自定义编码器。看一看 http://msdn.microsoft.com/en-us/library/ms751486.aspx


    劳伦斯·约翰斯顿补充道:

    这是我最后使用的代码的精华。我的MessageEncoder子类只是包装了一个MessageEncoder实例,并将除要读取消息的调用外的所有调用传递给包装的类。

    public override Message ReadMessage(ArraySegment<Byte> buffer, BufferManager bufferManager, String contentType) {
        // Convert buffer to stream and pass to overload.
        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) {
        // The only new functionality in this class is to pass the stream through a 
        // StreamReader with the encoding set to *not* throw on invalid bytes. 
        XmlReader reader = XmlReader.Create(new StreamReader(stream, new UTF8Encoding(false, false)));
        return Message.CreateMessage(reader, maxSizeOfHeaders, MessageVersion);
    }