Read the soap message body contents in WCF custom message filter - wcf

I've been working with WCF routing and implemented a custom message filter,
public override bool Match(Message message)
{
MessageBuffer buffer = message.CreateBufferedCopy(Int32.MaxValue);
var msg = buffer.CreateMessage();
XmlDictionaryReader reader = msg.GetReaderAtBodyContents();
string paramsXml = reader.ReadOuterXml();
....
....
return serviceType.Equals(service);
}
I'm getting the following exception "This message cannot support the operation because it has been copied." eventhough I'm creating a buffered copy. Can anyone help me on this?

This is apparently the problem with VS debugger. does not happen with soap ui or other clients. hope this will be useful for somebody struggling with same issue.

You need to set routeOnHeadersOnly = false in the routing behavior
Then you implement the operations that take message buffers

Related

Azure service bus Message deserialize broken in core conversion

So, I've created a new Azure Functions project v3 and am porting over a subset of functions from v1 that was running on 4.6.2, while retiring the rest as obsolete. Unfortunately in the change from BrokeredMessage to Message due to changing from Microsoft.ServiceBus.Messaging to Microsoft.Azure.ServiceBus the following deserialization method is now failing with:
There was an error deserializing the object of type stream. The input source is not correctly formatted.
The problem is right there in the error, but Im not sure what the correct new approach is, its a bit unclear.
Serialize
public static Message CreateBrokeredMessage(object messageObject)
{
var message = new Message(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(messageObject)))
{
ContentType = "application/json",
Label = messageObject.GetType().Name
};
return message;
}
Deserialize
public static T ParseBrokeredMessage<T>(Message msg)
{
var body = msg.GetBody<Stream>();
var jsonContent = new StreamReader(body, true).ReadToEnd();
T updateMessage = JsonConvert.DeserializeObject<T>(jsonContent);
return updateMessage;
}
Object
var fileuploadmessage = new PlanFileUploadMessage()
{
PlanId = file.Plan_Id.Value,
UploadedAt = uploadTimeStamp,
UploadedBy = uploadUser,
FileHash = uploadedFileName,
FileName = file.Name,
BusinessUnitName = businessUnitName,
UploadedFileId = uploadedFile.Id
};
```
Message.GetBody<T>() is an extension method for messages sent using the legacy Service Bus SDK (WindowsAzure.ServiceBus package) where BrokeredMessage was populated with anything other than Stream. If your sender sends an array of bytes as you've showed, you should access it using Message.Body property.
In case your message is sent as a BrokeredMessage, the receiving code will need to select either of the methods based on some information to indicate how the message was originally sent.

Tracking/Checking WCF client message

I send a message from my client (implements IClientMessageInspector) using BeforeSendRequest() and receive the reply from the endpoint at AfterReceiveReply().
My question is what is the most effective way to "validate" the reply that I receive is in relation to the request I sent?
I found some article about using the correlationstate, but the examples were all too vauge.
Any help is greatly appreciated.
As far as I know, we could use the Correlationstate parameter to maintain the value in order to represent the relativity.
That’s why the BeofreSendRequest has the returned value.
public void AfterReceiveReply(ref Message reply, object correlationState)
{
Console.WriteLine(correlationState.ToString());
string displayText = $"the client has received the reply:\n{reply}\n";
Console.Write(displayText);
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
var correlationstate = Guid.NewGuid().ToString();
string displayText = $"the client send request message:\n{request}\n";
Console.WriteLine(displayText);
return correlationstate;
}
Here is a related discussion, wish it is useful to you.
https://social.msdn.microsoft.com/Forums/vstudio/en-US/c8de85bf-9ffe-478e-a23c-2514a6504bce/iclientmessageinspector-maintain-id-value-between-the-beforesendrequest-and-afterreceivereply?forum=wcf
Feel free to let me know if there is anything I can help with.

Why does WCF ignore my TokenProvider?

I have a BizTalk WCF-Custom receive location to which I have added a custom behavior:
public class SasTokenProviderEndpointBehavior : BehaviorExtensionElement, IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(sharedAccessSecretName, sharedAccessKey);
bindingParameters.Add(new TransportClientEndpointBehavior { TokenProvider = tokenProvider });
}
}
}
parameter setup code omitted for brevity
This is adapted from a sample found at https://code.msdn.microsoft.com/How-to-integrate-BizTalk-07fada58#content - this author is widely respected in the BizTalk community and code of this kind has been in use for some years. All I am doing is adapting the method he uses, that is proven to work, to substitute a different TokenProvider.
I can see through debugging that this code runs and the TransportClientEndpointBehavior with correct parameters is added to the channel. However when the BizTalk receive location polls Service Bus, I see the following in the event log:
The adapter "WCF-Custom" raised an error message. Details "System.UnauthorizedAccessException: 40102: Missing authorization token, Resource:sb://[namespace].servicebus.windows.net/[queue]. TrackingId:452c2534-d3e6-400f-874f-09be324e9e11_G27, SystemTracker:[namespace].servicebus.windows.net:[queue], Timestamp:12/1/2016 11:38:56 AM ---> System.ServiceModel.FaultException: 40102: Missing authorization token, Resource:sb://[namespace].servicebus.windows.net/[queue]. TrackingId:452c2534-d3e6-400f-874f-09be324e9e11_G27, SystemTracker:[namespace].servicebus.windows.net:[queue], Timestamp:12/1/2016 11:38:56 AM
I cannot see any reason that the Azure Service Bus endpoint would return this error message except that because the token provider is not being used. Why would the channel ignore the TokenProvider and what do I have to do to pass the token correctly?
edit:
I have inspected the raw WCF message traffic for the port in question as well as one using the SB-Messaging adapter, which works as expected. The difference is that the SB-Messaging adapter's messages contain a SOAP header like:
<Authorization xmlns="http://schemas.microsoft.com/servicebus/2010/08/protocol/">SharedAccessSignature sr=[really long encoded string]</Authorization> and my custom binding port's messages do not. So it is true that the problem is a missing Authorization SOAP header; but the question persists - why isn't the channel adding this header?
edit #2:
I have decompiled Microsoft.ServiceBus.dll and I believe I've found the class that actually creates the WCF messsage, Microsoft.ServiceBus.Messaging.Sbmp.SbmpMessageCreator. It has this method:
private Message CreateWcfMessageInternal(string action, object body, bool includeToken, string parentLinkId, RetryPolicy policy, TrackingContext trackingContext, RequestInfo requestInfo)
{
Message message = Message.CreateMessage(this.messageVersion, action, body);
MessageHeaders headers = message.Headers;
headers.To = this.logicalAddress;
string sufficientClaims = this.GetSufficientClaims();
if (this.linkInfo != null)
{
if (!string.IsNullOrEmpty(this.linkInfo.TransferDestinationEntityAddress))
{
SecurityToken authorizationToken = this.GetAuthorizationToken(this.linkInfo.TransferDestinationEntityAddress, sufficientClaims);
if (authorizationToken != null)
{
SimpleWebSecurityToken webSecurityToken = (SimpleWebSecurityToken) authorizationToken;
if (webSecurityToken != null)
this.linkInfo.TransferDestinationAuthorizationToken = webSecurityToken.Token;
}
}
this.linkInfo.AddTo(headers);
}
if (includeToken)
{
ServiceBusAuthorizationHeader authorizationHeader = this.GetAuthorizationHeader(sufficientClaims);
if (authorizationHeader != null)
headers.Add((MessageHeader) authorizationHeader);
}
if (this.messagingFactory.FaultInjectionInfo != null)
this.messagingFactory.FaultInjectionInfo.AddToHeader(message);
if (!string.IsNullOrWhiteSpace(parentLinkId))
message.Properties["ParentLinkId"] = (object) parentLinkId;
if (trackingContext != null)
TrackingIdHeader.TryAddOrUpdate(headers, trackingContext.TrackingId);
MessageExtensionMethods.AddHeaderIfNotNull<RequestInfo>(message, "RequestInfo", "http://schemas.microsoft.com/netservices/2011/06/servicebus", requestInfo);
return message;
}
So thinking about it logically, there are two reasons the Authorization header would be missing:
includeToken is false (Why would this be so?)
GetAuthorizationHeader() returns null (Why?)
edit #3:
I have compiled and run the example code and this works. The only significant difference between my code and his is that mine includes a line which calls out to Azure Key Vault:
var kv = new KeyVaultClient(this.GetAccessToken);
var key = kv.GetSecretAsync(this.KeyVaultUri.AbsoluteUri, this.SharedAccessSecretName).Result;
var sharedAccessKey = key.Value;
var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(
this.SharedAccessSecretName,
sharedAccessKey);
bindingParameters.Add(new TransportClientEndpointBehavior { TokenProvider = tokenProvider });
This is an asynchronous method that returns a Task. Can it be that blocking on the result of this Task somehow doesn't do what would be expected in certain situations, and this is messing up the configuration of the WCF channel somehow? As I said, I am certain this code runs and assigns the TokenProvider. I am now merely not certain when it runs.
D'OH!
I had neglected to realise that the very old version of Microsoft.ServiceBus.dll we still have in the solution for interop with the (equally old) on premises version of Service Bus (Service Bus for Windows Server) was the one referenced by my project. For whatever reason this version just doesn't do what it's supposed to, and doesn't give any indication that it's bypassing the intended behaviour. Updating to have the current NuGet package for Service Bus fixes the problem.

WCF Routing with Content Transformation

I've implemented a WCF Routing service; I would also like the service (or a similar WCF service) to transform the payload in a prescribed and uniform (content-agnostic) fashion. For example, the payload will always take the form Foo<T> and I would like to pass it on as Bar<T> in all cases. I'm happy for the transformation to be XSLT or programmatic. I don't care what happens to messages received that aren't of the type Foo<T>.
I wish to use WCF as it provides a lot of OOTB functionality (e.g. its support for numerous bindings). It's not practical to implement a WCF service with numerous boilerplate methods to transform each closed generic (Foo<Class1> -> Bar<Class1>; Foo<Class2> -> Bar<Class2>; etc), as this would require recompilation/redeployment every time a new message type was to be routed.
To the best of my knowledge, WCF doesn't handle open generics and WCF Routing doesn't facilitate content transformation OOTB. That said, System.ServiceModel.Routing.RoutingService obviously intercepts WCF calls in some non-specific form, so I was hoping to leverage the same pattern to achieve my goal. Can anyone please provide direction on how to do this (or indicate why it's not possible)?
As I suggested in my comments on the question, there is a solution to this using the IDispatchMessageInspector. Please find below an extremely dumbed-down version of what I ended up writing (easier than me posting the code for 20 classes). If anyone wants a full solution implementing this code in a significantly cleaner and more advanced manner, let me know and I'll put my demo up on CodeProject. For now, I'll presume you're happy with a snippet of the guts.
The Console commands can obvious be removed (they're just so you can debug if you're self-hosting).
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
if (request == null || request.IsEmpty)
return null;
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(request);
Console.ResetColor();
// Load the request into a document.
XPathDocument document;
MemoryStream stream;
using (stream = new MemoryStream())
{
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream))
{
request.WriteMessage(writer);
writer.Flush();
stream.Position = 0L;
document = new XPathDocument(stream);
}
}
// Load the XSLT.
XslCompiledTransform transformer = new XslCompiledTransform();
transformer.Load("RequestTransformation.xslt");
// Transform the document.
byte[] transformedDocument;
using (stream = new MemoryStream())
{
transformer.Transform(document, null, stream);
transformedDocument = stream.ToArray();
}
// Construct new request from tranformed document.
stream = new MemoryStream(transformedDocument);
XmlReader reader = XmlReader.Create(stream);
Message modifiedMessage = Message.CreateMessage(reader, int.MaxValue, request.Version);
modifiedMessage.Properties.CopyProperties(request.Properties);
request = modifiedMessage;
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(new System.Text.UTF8Encoding(false).GetString(transformedDocument));
Console.ResetColor();
return null;
}

How to read the System.ServiceModel.Message?

I came across this situation.
Main Function:
Message msg = Message.CreateMessage(MessageVersion.Default, "Process");
String xmlData ="<Name>Navin</Name>";
Byte[] ba = Encoding.ASCII.GetBytes(xmlData);
MemoryStream ms = new MemoryStream(ba);
XmlWriter xw = XmlWriter.Create(ms);
msg.WriteBody(xw);
readMessage(msg);
In readMessage(Message msg):
XmlDictionaryReader xdr = msg.GetReaderAtBodyContents();
WHen i do this i am getting this error.
Unhandled Exception: System.InvalidOperationException: This message cannot suppo
rt the operation because it has been written.
How to overcome this.
Waiting for response.
Thanks in advance.
According to MSDN Message.GetReaderAtBodyContents Method, you can't access the message body once it's been read or written - it can only be accessed once. You need to use 'CreateBufferedCopy' to access a message multiple times.
I didn't find any examples in the MSDN documentation, but it looks like you'd need to create a MessageBuffer instance via Message.CreateBufferedCopy, and then you can use the MessageBuffer's CreateMessage method to gain access to the contents of the buffer.
See:
Message.CreateBufferedCopy Method
MessageBuffer Class
MessageBuffer.CreateMessage Method