dotnet / dotnet/wcf

Overloads of `Message.CreateMessage()` that take an `Xml(...)Reader` argument produce invalid body

Open
#5,717 3 comments 1 reaction 1 assignee Claimed by @mconnew View on GitHub
triaged
Dominant language
C#
Stars
1.8k
Forks
576
Avg merge
6d 9h
Merged PRs (30d)
2

Description

**Describe the bug**
I have the same issue as described at https://microsoft.public.dotnet.framework.webservices.narkive.com/n9xIRs39/modified-message-body-in-beforesendrequest-iclientmessagein-is-e. Calling any such overload invariably produces `...stream...` where `stream` is localized into what the runtime believes to be the culture of the machine instead of the body of the message. Now, I tried to follow the tip left on that page and create my own `Message` class overriding `OnWriteBodyContents(XmlDictionaryWriter writer)` only to find out that it is NOT called by WCF at all. However, somehow it now outputs just `...` in the same place.

**To Reproduce**
```C#
public class CorrectorInspector: IClientMessageInspector
{
private class MyMessage: Message
{
private XmlReader xmlReader1;
private Message request;

public MyMessage(XmlReader xmlReader1, Message request)
{
this.xmlReader1 = xmlReader1;
this.request = request;
}

public override MessageHeaders Headers => this.request.Headers;

public override MessageProperties Properties => this.request.Properties;

public override MessageVersion Version => this.request.Version;

protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
// Breakpoints are not hit.
var _reader = this.xmlReader1;
using(_reader)
{
var type = _reader.MoveToContent();
while(!_reader.EOF && type != XmlNodeType.EndElement)
{
if(type != XmlNodeType.Element)
throw new InvalidOperationException();
writer.WriteNode(_reader, false);

type = _reader.MoveToContent();
}
}
}

}

private static readonly XNamespace urn = "urn:service";
private static readonly XNamespace env = "http://schemas.xmlsoap.org/soap/envelope/";
public void AfterReceiveReply(ref Message reply, object correlationState) { }

public object? BeforeSendRequest(ref Message request, IClientChannel channel)
{
var msgbuf = request.CreateBufferedCopy(int.MaxValue);

using var tmpMessage = msgbuf.CreateMessage();
using var ms = new MemoryStream();
using var writer = XmlWriter.Create(ms, new XmlWriterSettings { OmitXmlDeclaration = true, NamespaceHandling = NamespaceHandling.OmitDuplicates });
tmpMessage.WriteMessage(writer);
writer.Flush();

ms.Position = 0;
var xml = XDocument.Load(ms);

xml.Root!.Add(new XAttribute(XNamespace.Xmlns + "urn", urn));

var header = xml.Root.Element(env + "Header")!;
header.RemoveNodes();

var body = xml.Root.Element(env + "Body")!;
body.RemoveAttributes();

var action = (XElement)body.FirstNode!;
action.RemoveAttributes();
action.Name = urn + action.Name.LocalName;
foreach(var param in action.Elements())
{
param.Name = param.Name.LocalName;
param.RemoveAttributes();
}

var newMessage = new MyMessage(xml.CreateReader(), request);
// Previously the above line was
//var newRequest = Message.CreateMessage(xml.CreateReader(), int.MaxValue, request.Version);
//newMessage.Headers.CopyHeadersFrom(oldMessage);
//newRequest.Properties.CopyProperties(request.Properties);
request = newMessage; // Breakpoint at this line reveals upon inspection of `newMessage` that the body tag has the unexpected value.
return null;
}
}
```

**Expected behavior**
The message body, as a part of the whole message, is correctly replaced with the new XML.

**Screenshots**
I can do on request.

**Additional context**
The current behavior might be by design. https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.channels.message.createmessage?view=net-8.0-pp&devlangs=csharp&f1url=%3FappId%3DDev17IDEF1%26l%3DEN-US%26k%3Dk%28System.ServiceModel.Channels.Message.CreateMessage%29%3Bk%28DevLang-csharp%29%26rd%3Dtrue#system-servicemodel-channels-message-createmessage(system-xml-xmlreader-system-int32-system-servicemodel-channels-messageversion) remarks that it reads up to the body of the message. However, it doesn't explain how are we supposed to provide the body of the message then, neither there is any API that I could find myself for that, and all my Internet search results say in unison that this is how you change the message in an inspector w/o mentioning this pitfall, nor can I see how is outputting the literal string of `...stream...` (localized) in the modified response is helpful, and the other overloads taking an `Xml(...)Reader` instance do not have this note, but are still affected all the same. Either way, there is an issue to be resolved.

I have tried many different combinations of streams and writers in every overload.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.