问题 配置.NET WCF UTF-8反序列化器来修改/丢弃非最短形式的字符而不是抛出异常?


我们有一个通过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

有人能指出我正确的地方和方法来覆盖默认的异常回退与替换回退?


3470
2017-11-24 18:30


起源



答案:


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


劳伦斯约翰斯顿补充道:

这是我最终使用的代码的核心。我的MessageEncoder子类只包装一个MessageEncoder实例,并将除ReadMessage之外的所有调用传递给包装类。

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

10
2017-11-24 20:40



谢谢!实际上我通过实现一个自定义编码器的方式实际上大约有90%,但遇到了一个路障,这个链接清理得很好。我要编辑你的答案,添加我最终使用的代码,以便答案都集中在一个地方。 - Lawrence Johnston


答案:


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


劳伦斯约翰斯顿补充道:

这是我最终使用的代码的核心。我的MessageEncoder子类只包装一个MessageEncoder实例,并将除ReadMessage之外的所有调用传递给包装类。

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

10
2017-11-24 20:40



谢谢!实际上我通过实现一个自定义编码器的方式实际上大约有90%,但遇到了一个路障,这个链接清理得很好。我要编辑你的答案,添加我最终使用的代码,以便答案都集中在一个地方。 - Lawrence Johnston