2010-11-24 37 views
11

Nous avons un service web SOAP hébergé via WCF.Configuration du désérialiseur .NET WCF UTF-8 pour modifier/supprimer les caractères de forme non les plus courts au lieu de générer une exception?

L'un des clients à qui nous recevons des données peut occasionnellement encoder l'UTF-8 en utilisant la forme la plus courte (voir http://www.unicode.org/versions/corrigendum1.html pour un peu d'information à ce sujet).

Il n'est pas facile de modifier le client, car ces caractères ne sont pas encodés par notre code. A la place, nous aimerions éditer le service WCF pour supprimer ces caractères, les remplacer par d'autres caractères substitués, ou même accepter les caractères de forme non les plus courts. Tout d'entre eux serait acceptable pour notre cas d'utilisation, bien que les anciennes options seraient préférées car elles réduisent tout risque de sécurité.

En regardant la trace de la pile:

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) 

Il ressemble à ce que nous voulons est de passer le fallback décodeur System.Text.DecoderExceptionFallback-System.Text.DecoderReplacementFallback. Est-ce que quelqu'un peut me diriger vers le bon endroit et la manière de remplacer le repli d'exception par défaut avec un repli de remplacement?

Répondre

10

Une possibilité à explorer est la création d'un encodeur personnalisé. Jetez un oeil à http://msdn.microsoft.com/en-us/library/ms751486.aspx


Ajouté par Lawrence Johnston:

C'est la viande du code que je fini par utiliser. Ma sous-classe MessageEncoder encapsule simplement une instance de MessageEncoder et transmet tous les appels sauf ceux vers ReadMessage à la classe enveloppée.

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

Merci beaucoup! En fait, j'étais en train de mettre en place un encodeur personnalisé à environ 90% du temps, mais je me suis heurté à un barrage routier que ce lien a bien nettoyé. Je vais modifier votre réponse pour ajouter le code que j'ai fini par utiliser afin que la réponse soit en un seul endroit. –