WCF中IDispatchMessageInspector如何修复传入的错误XML?
Your error occurs because the incoming request contains an invalid XML structure: an XML declaration (<?xml version="1.0" encoding="utf-8"?>) nested inside a <data> element, which violates XML standards. Since you can't modify the request source, you need to intercept and fix the message before WCF parses it.
Why Your Previous Attempts Failed
request.GetReaderAtBodyContents()andrequest.GetBody<string>()throw exceptions because they attempt to parse the invalid XML structure.request.ToString()returns a formatted representation of the message, not the raw XML content, so string replacement here is unreliable.
Working Solution
Use IDispatchMessageInspector.AfterReceiveRequest (or IDispatchOperationSelector.SelectOperation since it runs earlier) to read the raw request body, fix the invalid XML, and replace the original message with a corrected version:
using System; using System.IO; using System.Text; using System.Text.RegularExpressions; using System.Xml; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Dispatcher; public class InvalidXmlFixInspector : IDispatchMessageInspector { public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { if (request == null || request.IsFault) return null; // Access HTTP request properties to get raw body stream if (request.Properties.ContainsKey(HttpRequestMessageProperty.Name)) { var httpRequestProp = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name]; Stream originalStream = httpRequestProp.GetBody(); // Buffer the stream to read it multiple times MemoryStream bufferedStream = new MemoryStream(); originalStream.CopyTo(bufferedStream); bufferedStream.Position = 0; // Determine request encoding from ContentType header Encoding encoding = Encoding.UTF8; string contentType = httpRequestProp.ContentType; if (!string.IsNullOrEmpty(contentType)) { var charsetMatch = Regex.Match(contentType, @"charset=([^;]+)", RegexOptions.IgnoreCase); if (charsetMatch.Success) { encoding = Encoding.GetEncoding(charsetMatch.Groups[1].Value); } } // Read raw body content string rawBody = new StreamReader(bufferedStream, encoding).ReadToEnd(); bufferedStream.Position = 0; // Remove any embedded XML declarations (robust regex match) string correctedBody = Regex.Replace(rawBody, @"<\?xml\s+version=""[^""]+""\s+encoding=""[^""]+""\?>", string.Empty, RegexOptions.IgnoreCase); // Create corrected message stream using (var correctedStream = new MemoryStream(encoding.GetBytes(correctedBody))) { using (var xmlReader = XmlReader.Create(correctedStream)) { // Create new valid message Message correctedMessage = Message.CreateMessage(xmlReader, int.MaxValue, request.Version); // Copy all original message properties to preserve context foreach (var prop in request.Properties) { if (!correctedMessage.Properties.ContainsKey(prop.Key)) { correctedMessage.Properties.Add(prop.Key, prop.Value); } } // Replace original request with corrected message request = correctedMessage; } } // Update HTTP request property with buffered stream for other components httpRequestProp.SetBody(bufferedStream); } return null; } public void BeforeSendReply(ref Message reply, object correlationState) { // No action needed here } }
Key Notes
- Buffering the Stream: Ensures we can read the request content multiple times without losing data.
- Encoding Handling: Uses the actual encoding specified in the request's
ContentTypeheader instead of assuming UTF-8. - Robust Regex: Matches any standard XML declaration (case-insensitive, handles variations in quotes/attributes) to avoid missing edge cases.
- Property Copying: Preserves all original message properties (like HTTP headers) to maintain request context.
Using IDispatchOperationSelector
If you prefer to intercept the message earlier via IDispatchOperationSelector, the core logic remains identical—access the raw request stream, fix the XML, and replace the message with the corrected version.
内容的提问来源于stack exchange,提问作者zig8953

