How to intercept SOAP messages - wcf

As part of my venture into WCF I'm looking at message contracts and seeing how they affect the content of the SOAP message.
What would be really cool is if you could intercept the message and see how it is structured. How would I go about doing this please...
(So far I've looked at Wireshark (too 'low-level') and thought about Microsoft SOAP toolkit but this was retired by microsoft back in 2005)

When you installed .NET 3.5 or up, you should have the WCF Test Client on your machine somewhere (hidden deep inside a directory like C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ or something like that).
This tool allows you to connect to your WCF service, and you can call methods on it - and you can look at the XML request and response in all its beauty :-)
The other option would be to use something like the free version of SoapUI which is designed to test SOAP services and show request and response in XML
SoapUI is a great tool - but it's not WCF specific, it's just a "generic" SOAP/WSDL tool which works great against any SOAP service.
If you're not looking for "on-demand" capturing of requests and responses, but if you're more interested in having a trace of all requests and responses, you should investigate the WCF tracing features and setup them up as needed. You can capture all traffic into a *.svclog file on disk, and there's the WCF Service Trace Viewer (also free with WCF) to inspect those trace files.

I usually use Fiddler to inspect soap messages sent over http.

If you want to write a log record is try using TraceExtension class, below an example,
in this link you will find details about how to implement it, I use it and it worked very well
http://www.systemdeveloper.info/2013/11/trace-soap-requestresponse-xml-with.html
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Services.Protocols;
using System.IO;
using System.Xml;
namespace PruebaServiciosNBC
{
class TraceExtension : SoapExtension
{
private Stream oldStream;
private Stream newStream;
private static XmlDocument xmlRequest;
/// <summary>
/// Gets the outgoing XML request sent to PayPal
/// </summary>
public static XmlDocument XmlRequest
{
get { return xmlRequest; }
}
private static XmlDocument xmlResponse;
/// <summary>
/// Gets the incoming XML response sent from PayPal
/// </summary>
public static XmlDocument XmlResponse
{
get { return xmlResponse; }
}
/// <summary>
/// Save the Stream representing the SOAP request
/// or SOAP response into a local memory buffer.
/// </summary>
/// <param name="stream">
/// <returns></returns>
public override Stream ChainStream(Stream stream)
{
oldStream = stream;
newStream = new MemoryStream();
return newStream;
}
/// <summary>
/// If the SoapMessageStage is such that the SoapRequest or
/// SoapResponse is still in the SOAP format to be sent or received,
/// save it to the xmlRequest or xmlResponse property.
/// </summary>
/// <param name="message">
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
xmlRequest = GetSoapEnvelope(newStream);
CopyStream(newStream, oldStream);
break;
case SoapMessageStage.BeforeDeserialize:
CopyStream(oldStream, newStream);
xmlResponse = GetSoapEnvelope(newStream);
break;
case SoapMessageStage.AfterDeserialize:
break;
}
}
/// <summary>
/// Returns the XML representation of the Soap Envelope in the supplied stream.
/// Resets the position of stream to zero.
/// </summary>
/// <param name="stream">
/// <returns></returns>
private XmlDocument GetSoapEnvelope(Stream stream)
{
XmlDocument xml = new XmlDocument();
stream.Position = 0;
StreamReader reader = new StreamReader(stream);
xml.LoadXml(reader.ReadToEnd());
stream.Position = 0;
return xml;
}
/// <summary>
/// Copies a stream.
/// </summary>
/// <param name="from">
/// <param name="to">
private void CopyStream(Stream from, Stream to)
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
#region NoOp
/// <summary>
/// Included only because it must be implemented.
/// </summary>
/// <param name="methodInfo">
/// <param name="attribute">
/// <returns></returns>
public override object GetInitializer(LogicalMethodInfo methodInfo,
SoapExtensionAttribute attribute)
{
return null;
}
/// <summary>
/// Included only because it must be implemented.
/// </summary>
/// <param name="WebServiceType">
/// <returns></returns>
public override object GetInitializer(Type WebServiceType)
{
return null;
}
/// <summary>
/// Included only because it must be implemented.
/// </summary>
/// <param name="initializer">
public override void Initialize(object initializer)
{
}
#endregion NoOp
}
}

Related

how to call my api qnamaker using Microsoft.Azure.CognitiveServices.Knowledge.QnAMaker nuget version 1.0.0

I am getting started with Microsoft.Azure.CognitiveServices.Knowledge.QnAMaker nuget,
and I am trying to use QnAMakerClient() class to initialise a new instance of the QNAMakerClient class.
But this class takes abstract parameters :
public QnAMakerClient(ServiceClientCredentials credentials, params DelegatingHandler[] handlers).
I found some solution
https://csharp.hotexamples.com/examples/Microsoft.Rest/TokenCredentials/-/php-tokencredentials-class-examples.html which indicates how to get the credentials token.
As I am new to this concept, so I don't know how to get credentials token for my ServiceClientCredentials.
You can create your own TokenCredentials class which inherit from ServiceClientCredentials as shown below:
public class TokenCredentials : ServiceClientCredentials
{
/// <summary>
/// The bearer token type, as serialized in an http Authentication header.
/// </summary>
private const string BearerTokenType = "Bearer";
/// <summary>
/// Gets or sets secure token used to authenticate against Microsoft Azure API.
/// No anonymous requests are allowed.
/// </summary>
protected ITokenProvider TokenProvider { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="TokenCredentials"/>
/// class with the given 'Bearer' token.
/// </summary>
/// <param name="token">Valid JSON Web Token (JWT).</param>
public TokenCredentials(string token)
: this(token, BearerTokenType)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TokenCredentials"/>
/// class with the given token and token type.
/// </summary>
/// <param name="token">Valid JSON Web Token (JWT).</param>
/// <param name="tokenType">The token type of the given token.</param>
public TokenCredentials(string token, string tokenType)
: this(new StringTokenProvider(token, tokenType))
{
if (string.IsNullOrEmpty(token))
{
throw new ArgumentNullException("token");
}
if (string.IsNullOrEmpty(tokenType))
{
throw new ArgumentNullException("tokenType");
}
}
/// <summary>
/// Create an access token credentials object, given an interface to a token source.
/// </summary>
/// <param name="tokenProvider">The source of tokens for these credentials.</param>
public TokenCredentials(ITokenProvider tokenProvider)
{
if (tokenProvider == null)
{
throw new ArgumentNullException("tokenProvider");
}
this.TokenProvider = tokenProvider;
}
/// <summary>
/// Apply the credentials to the HTTP request.
/// </summary>
/// <param name="request">The HTTP request.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>
/// Task that will complete when processing has completed.
/// </returns>
public async override Task ProcessHttpRequestAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
if (TokenProvider == null)
{
throw new InvalidOperationException(Resources.TokenProviderCannotBeNull);
}
request.Headers.Authorization = await TokenProvider.GetAuthenticationHeaderAsync(cancellationToken);
await base.ProcessHttpRequestAsync(request, cancellationToken);
}
}
This is a good starting point for knowing more about QnAMaker.
Hope this helps!!!

Unable to deserialize CSLA request in soap message sent by WCF (C#)

I am learning WCF so my apologies if my terminology is off here and there.
I am trying to deserialize a soap message that was sent with WCF and captured by Fiddler and I keep getting this error message: "The data at the root level is invalid. Line 1, position 1."
The WCF service is setup with the following code:
// Set up WcfProxy DataPortal service url
Csla.DataPortalClient.WcfProxy.DefaultUrl = appRootUrl + "WcfSilverlightPortal.svc";
// Set up WcfProxy Binding
System.ServiceModel.BasicHttpBinding largeBinding = new System.ServiceModel.BasicHttpBinding();
largeBinding.MaxReceivedMessageSize = 20000000;
largeBinding.MaxBufferSize = 20000000;
largeBinding.OpenTimeout = new TimeSpan(0, 10, 0);
largeBinding.ReceiveTimeout = new TimeSpan(0, 10, 0);
largeBinding.CloseTimeout = new TimeSpan(0, 10, 0);
largeBinding.SendTimeout = new TimeSpan(0, 10, 0);
largeBinding.Name = "BasicHttpBinding_IWcfPortal";
Csla.DataPortalClient.WcfProxy.DefaultBinding = largeBinding;
The soap message captured in Fiddler is as follows:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<Fetch xmlns="http://ws.lhotka.net/WcfDataPortal">
<request xmlns:d4p1="http://schemas.datacontract.org/2004/07/Csla.Server.Hosts.Silverlight" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<d4p1:ClientContext>QBhBcnJheU9mU2VyaWFsaXphdGlvbkluZm8IQWh0dHA6Ly9zY2hlbWFzLmRhdGFjb250cmFjdC5vcmcvMjAwNC8wNy9Dc2xhLlNlcmlhbGl6YXRpb24uTW9iaWxlCQFpKWh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlQBFTZXJpYWxpemF0aW9uSW5mb0AIQ2hpbGRyZW4JAWE5aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS8yMDAzLzEwL1NlcmlhbGl6YXRpb24vQXJyYXlzAUALUmVmZXJlbmNlSWSDQAhUeXBlTmFtZZhlQ3NsYS5Db3JlLkNvbnRleHREaWN0aW9uYXJ5LCBDc2xhLCBWZXJzaW9uPTQuMy4xMy4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTkzYmU1ZmRjMDkzZTRjMzABQAZWYWx1ZXMJAWE5aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS8yMDAzLzEwL1NlcmlhbGl6YXRpb24vQXJyYXlzAQEB</d4p1:ClientContext>
<d4p1:ClientCulture>en-US</d4p1:ClientCulture>
<d4p1:ClientUICulture>en-US</d4p1:ClientUICulture>
<d4p1:CriteriaData i:nil="true" />
<d4p1:GlobalContext>QBhBcnJheU9mU2VyaWFsaXphdGlvbkluZm8IQWh0dHA6Ly9zY2hlbWFzLmRhdGFjb250cmFjdC5vcmcvMjAwNC8wNy9Dc2xhLlNlcmlhbGl6YXRpb24uTW9iaWxlCQFpKWh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlQBFTZXJpYWxpemF0aW9uSW5mb0AIQ2hpbGRyZW4JAWE5aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS8yMDAzLzEwL1NlcmlhbGl6YXRpb24vQXJyYXlzAUALUmVmZXJlbmNlSWSDQAhUeXBlTmFtZZhlQ3NsYS5Db3JlLkNvbnRleHREaWN0aW9uYXJ5LCBDc2xhLCBWZXJzaW9uPTQuMy4xMy4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTkzYmU1ZmRjMDkzZTRjMzABQAZWYWx1ZXMJAWE5aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS8yMDAzLzEwL1NlcmlhbGl6YXRpb24vQXJyYXlzAQEB</d4p1:GlobalContext>
<d4p1:Principal>QBhBcnJheU9mU2VyaWFsaXphdGlvbkluZm8IQWh0dHA6Ly9zY2hlbWFzLmRhdGFjb250cmFjdC5vcmcvMjAwNC8wNy9Dc2xhLlNlcmlhbGl6YXRpb24uTW9iaWxlCQFpKWh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlQBFTZXJpYWxpemF0aW9uSW5mb0AIQ2hpbGRyZW4JAWE5aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS8yMDAzLzEwL1NlcmlhbGl6YXRpb24vQXJyYXlzAUALUmVmZXJlbmNlSWSDQAhUeXBlTmFtZZhwQ3NsYS5TZWN1cml0eS5VbmF1dGhlbnRpY2F0ZWRQcmluY2lwYWwsIENzbGEsIFZlcnNpb249NC4zLjEzLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49OTNiZTVmZGMwOTNlNGMzMAFABlZhbHVlcwkBYTlodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tLzIwMDMvMTAvU2VyaWFsaXphdGlvbi9BcnJheXNeM0tleVZhbHVlT2ZzdHJpbmdTZXJpYWxpemF0aW9uSW5mby5GaWVsZERhdGFPem9adkxybV4DS2V5mRZDc2xhUHJpbmNpcGFsLklkZW50aXR5XgVWYWx1ZUAMRW51bVR5cGVOYW1lLgNuaWyGAUAHSXNEaXJ0eYVABVZhbHVlLgR0eXBlmA5iOmJhc2U2NEJpbmFyeQkBYiBodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYaCNCUAYQXJyYXlPZlNlcmlhbGl6YXRpb25JbmZvCEFodHRwOi8vc2NoZW1hcy5kYXRhY29udHJhY3Qub3JnLzIwMDQvMDcvQ3NsYS5TZXJpYWxpemF0aW9uLk1vYmlsZQkBaSlodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZUARU2VyaWFsaXphdGlvbkluZm9ACENoaWxkcmVuCQFhOWh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vMjAwMy8xMC9TZXJpYWxpemF0aW9uL0FycmF5c14zS2V5VmFsdWVPZnN0cmluZ1NlcmlhbGl6YXRpb25JbmZvLkNoaWxkRGF0YU96b1p2THJtXgNLZXmZDV9maWVsZE1hbmFnZXJeBVZhbHVlQAdJc0RpcnR5hUALUmVmZXJlbmNlSWSJAgEBAUALUmVmZXJlbmNlSWSDQAhUeXBlTmFtZZhvQ3NsYS5TZWN1cml0eS5VbmF1dGhlbnRpY2F0ZWRJZGVudGl0eSwgQ3NsYSwgVmVyc2lvbj00LjMuMTMuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj05M2JlNWZkYzA5M2U0YzMwAUAGVmFsdWVzCQFhOWh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vMjAwMy8xMC9TZXJpYWxpemF0aW9uL0FycmF5cwEBQBFTZXJpYWxpemF0aW9uSW5mb0AIQ2hpbGRyZW4JAWE5aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS8yMDAzLzEwL1NlcmlhbGl6YXRpb24vQXJyYXlzXjNLZXlWYWx1ZU9mc3RyaW5nU2VyaWFsaXphdGlvbkluZm8uQ2hpbGREYXRhT3pvWnZMcm1eA0tleZkFUm9sZXNeBVZhbHVlQAdJc0RpcnR5hUALUmVmZXJlbmNlSWSJAwEBAUALUmVmZXJlbmNlSWSJAkAIVHlwZU5hbWWYcUNzbGEuQ29yZS5GaWVsZE1hbmFnZXIuRmllbGREYXRhTWFuYWdlciwgQ3NsYSwgVmVyc2lvbj00LjMuMTMuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj05M2JlNWZkYzA5M2U0YzMwAUAGVmFsdWVzCQFhOWh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vMjAwMy8xMC9TZXJpYWxpemF0aW9uL0FycmF5c14zS2V5VmFsdWVPZnN0cmluZ1NlcmlhbGl6YXRpb25JbmZvLkZpZWxkRGF0YU96b1p2THJtXgNLZXmZE19idXNpbmVzc09iamVjdFR5cGVeBVZhbHVlQAxFbnVtVHlwZU5hbWUuA25pbIYBQAdJc0RpcnR5hUAFVmFsdWUuBHR5cGWYCGI6c3RyaW5nCQFiIGh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hmG9Dc2xhLlNlY3VyaXR5LlVuYXV0aGVudGljYXRlZElkZW50aXR5LCBDc2xhLCBWZXJzaW9uPTQuMy4xMy4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTkzYmU1ZmRjMDkzZTRjMzABAQFeM0tleVZhbHVlT2ZzdHJpbmdTZXJpYWxpemF0aW9uSW5mby5GaWVsZERhdGFPem9adkxybV4DS2V5mRJBdXRoZW50aWNhdGlvblR5cGVeBVZhbHVlQAxFbnVtVHlwZU5hbWUuA25pbIYBQAdJc0RpcnR5hUAFVmFsdWUuBHR5cGWYCGI6c3RyaW5nCQFiIGh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hAQEBXjNLZXlWYWx1ZU9mc3RyaW5nU2VyaWFsaXphdGlvbkluZm8uRmllbGREYXRhT3pvWnZMcm1eA0tleZkPSXNBdXRoZW50aWNhdGVkXgVWYWx1ZUAMRW51bVR5cGVOYW1lLgNuaWyGAUAHSXNEaXJ0eYVABVZhbHVlLgR0eXBlmAliOmJvb2xlYW4JAWIgaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWGFAQFeM0tleVZhbHVlT2ZzdHJpbmdTZXJpYWxpemF0aW9uSW5mby5GaWVsZERhdGFPem9adkxybV4DS2V5mQROYW1lXgVWYWx1ZUAMRW51bVR5cGVOYW1lLgNuaWyGAUAHSXNEaXJ0eYVABVZhbHVlLgR0eXBlmAhiOnN0cmluZwkBYiBodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYQEBAQEBQBFTZXJpYWxpemF0aW9uSW5mb0AIQ2hpbGRyZW4JAWE5aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS8yMDAzLzEwL1NlcmlhbGl6YXRpb24vQXJyYXlzAUALUmVmZXJlbmNlSWSJA0AIVHlwZU5hbWWYvkNzbGEuQ29yZS5Nb2JpbGVMaXN0YDFbW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTUuMC41LjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49N2NlYzg1ZDdiZWE3Nzk4ZV1dLCBDc2xhLCBWZXJzaW9uPTQuMy4xMy4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTkzYmU1ZmRjMDkzZTRjMzABQAZWYWx1ZXMJAWE5aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS8yMDAzLzEwL1NlcmlhbGl6YXRpb24vQXJyYXlzXjNLZXlWYWx1ZU9mc3RyaW5nU2VyaWFsaXphdGlvbkluZm8uRmllbGREYXRhT3pvWnZMcm1eA0tleZkFJGxpc3ReBVZhbHVlQAxFbnVtVHlwZU5hbWUuA25pbIYBQAdJc0RpcnR5hUAFVmFsdWUuBHR5cGWYDmI6YmFzZTY0QmluYXJ5CQFiIGh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hnoc8QXJyYXlPZnN0cmluZyB4bWxuczppPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vMjAwMy8xMC9TZXJpYWxpemF0aW9uL0FycmF5cyIgLz4BAQEBAZ8BAQEBAQEB</d4p1:Principal>
<d4p1:TypeName>Competition.Domain.Entities.SchoolSummaryList, Competition.Domain.Csla, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</d4p1:TypeName>
</request>
</Fetch>
</s:Body>
</s:Envelope>
I am trying to deserialize this to an object of type Csla.Server.Hosts.Silverlight.CriteriaRequest
The code for this class looks like this:
//-----------------------------------------------------------------------
// <copyright file="CriteriaRequest.cs" company="Marimer LLC">
// Copyright (c) Marimer LLC. All rights reserved.
// Website: http://www.lhotka.net/cslanet/
// </copyright>
// <summary>Message sent to the Silverlight</summary>
//-----------------------------------------------------------------------
using System;
using System.Runtime.Serialization;
using System.Security.Principal;
using Csla.Core;
namespace Csla.Server.Hosts.Silverlight
{
/// <summary>
/// Message sent to the Silverlight
/// WCF data portal.
/// </summary>
[DataContract]
public class CriteriaRequest
{
/// <summary>
/// Assembly qualified name of the
/// business object type to create.
/// </summary>
[DataMember]
public string TypeName { get; set; }
/// <summary>
/// Serialized data for the criteria object.
/// </summary>
[DataMember]
public byte[] CriteriaData { get; set; }
/// <summary>
/// Serialized data for the principal object.
/// </summary>
[DataMember]
public byte[] Principal { get; set; }
/// <summary>
/// Serialized data for the global context object.
/// </summary>
[DataMember]
public byte[] GlobalContext { get; set; }
/// <summary>
/// Serialized data for the client context object.
/// </summary>
[DataMember]
public byte[] ClientContext { get; set; }
/// <summary>
/// Serialized client culture.
/// </summary>
/// <value>The client culture.</value>
[DataMember]
public string ClientCulture { get; set; }
/// <summary>
/// Serialized client UI culture.
/// </summary>
/// <value>The client UI culture.</value>
[DataMember]
public string ClientUICulture { get; set; }
}
}
I have tried grabbing each of the three xml elements 'Body', 'Fetch' and 'request' from the soap message and storing them as an XElement variable named 'element' and running the code below:
DataContractSerializer dcs = new DataContractSerializer(typeof(CriteriaRequest));
MemoryStream ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(element.Value));
ms.Seek(0, SeekOrigin.Begin);
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(ms, Encoding.UTF8, new XmlDictionaryReaderQuotas(), null);
CriteriaRequest fr = (CriteriaRequest)dcs.ReadObject(reader);
In every case it has failed to deserialize the object and I am out of ideas.
I think I have provided all the relavent information but if anything else would help please let me know and I will provide it.
Thanks.
You can't use the more simplistic serializers to directly serialize a CSLA .NET object graph. Serializers such as XmlSerializer, JsonSerializer, and the various DataContractSerializer variations aren't sophisticated enough to completely clone and object graph, and so they aren't supported by CSLA.
Only the BinaryFormatter, NetDataContractSerializer, and MobileFormatter are supported.
Odds are you are trying to expose your business objects directly as a service interface. That's a poor architectural choice, and one I'd never choose to support or encourage anyone to do. You might find the XML Services FAQ page useful.

Silverlight is not liking my WCF MessageContract. Why?

I am attempting to upload a file through a Silverlight client using the following MessageContract:
[MessageContract]
public class CategoryClientFileTransferMC : IDisposable
{
/// <summary>
/// CategoryID - Category identity.
/// </summary>
[MessageHeader(MustUnderstand = true)]
public int CategoryID;
/// <summary>
/// ID - File identifier.
/// </summary>
[MessageHeader(MustUnderstand = true)]
public string ID;
/// <summary>
/// Length - File length in bytes.
/// </summary>
[MessageHeader(MustUnderstand = true)]
public long Length;
/// <summary>
/// FileByteStream - File stream.
/// </summary>
[MessageBodyMember(Order = 1)]
public Stream FileByteStream;
/// <summary>
/// Dispose the contract.
/// </summary>
public void Dispose()
{
if (FileByteStream != null)
{
FileByteStream.Close();
FileByteStream = null;
}
}
}
My problem is that the generated operation method on the client only takes a single argument; a byte array called FileByteStream. In other (non-Silverlight) clients I've created it asks for the MemberHeader fields as well. Without specifying these headers, the server has no idea what to do with the file. How can I set these headers when I call the operation?
Also, is there a better way to upload a file from a Silverlight client? This has been a huge headache.
Thanks.
The Silverlight subset of the WCF client does not support the [MessageHeader] attribute. You can still set message headers, but it's not as straightforward as in other platforms. Basically, you'll need to set the headers using the operation context, prior to making the call, like in the example below:
var client = new SilverlightReference1.MyClient();
using (new OperationContextScope(client.InnerChannel))
{
string contractNamespace = "http://tempuri.org/";
OperationContext.Current.OutgoingMessageHeaders.Add(
MessageHeader.CreateHeader("CategoryId", contractNamespace, 1));
OperationContext.Current.OutgoingMessageHeaders.Add(
MessageHeader.CreateHeader("ID", contractNamespace, "abc123"));
OperationContext.Current.OutgoingMessageHeaders.Add(
MessageHeader.CreateHeader("Length", contractNamespace, 123456L));
client.UploadFile(myFileContents);
}
Where contractNamespace is the XML namespace for the message header fields (IIRC they default to the same as the service contract). You can use Fiddler and something like the WCF Test Client to see which namespace is used there.

Content-Type header value missing after upgrade to WCF Data Services 5.0

After upgrading to WCF Data Services 5.0 for OData V3 i can't make insert or updates.
The only thing that is possible are selects.
EDIT
Exception in overridden method HandleException:
Exception:
System.Data.Services.DataServiceException: Content-Type header value missing.
at System.Data.Services.HttpProcessUtility.ReadContentType(String contentType, String& mime, Encoding& encoding)
at System.Data.Services.Serializers.Deserializer.CreateDeserializer(RequestDescription description, IDataService dataService, Boolean update, UpdateTracker tracker)
at System.Data.Services.DataService1.HandlePostOperation(RequestDescription description, IDataService dataService)
at System.Data.Services.DataService1.ProcessIncomingRequest(RequestDescription description, IDataService dataService)
at System.Data.Services.DataService1.HandleNonBatchRequest(RequestDescription description)
at System.Data.Services.DataService1.HandleRequest()
ResponseStatusCode: 400
Client-Code
I set a token in the HTTP-Request-Header...
/// <summary>
/// Initializes a new instance of the <see cref="ServiceBase"/> class.
/// </summary>
/// <param name="uri">service uri</param>
protected ServiceBase(Uri uri)
{
this.Context = new Entities(uri) { MergeOption = MergeOption.OverwriteChanges };
this.Context.SendingRequest += new EventHandler<SendingRequestEventArgs>(this.ContextSendingRequest);
}
/// <summary>
/// OnSendingRequest
/// </summary>
/// <param name="sender">source</param>
/// <param name="e">event args</param>
private void ContextSendingRequest(object sender, SendingRequestEventArgs e)
{
e.RequestHeaders["token"] = "xyassdfdfdfdf";
}
Service-Code:
The Service parses this Header and checks the token
public XYDataService()
{
this.ProcessingPipeline.ProcessingRequest += this.ProcessingPipelineProcessingRequest;
}
/// <summary>
/// OnProcessingRequest
/// </summary>
/// <param name="sender">source</param>
/// <param name="e">event args</param>
private void ProcessingPipelineProcessingRequest(object sender, DataServiceProcessingPipelineEventArgs e)
{
var authHeader = HttpContext.Current.Request.Headers["token"];
if (string.IsNullOrEmpty(authHeader) || !authHeader.Equals("xyassdfdfdfdf"))
{
throw new DataServiceException(401, "401 Unauthorized");
}
}
Thanks for your help.
Andi
Using answer since I need to put more text here.
I tried a very similar code to yours, but it works just fine:
DemoService ctx = new DemoService(new Uri("http://services.odata.org/(S(jcemln1vp0u1gqtoyqqpwrc1))/OData/OData.svc/"));
ctx.UsePostTunneling = true;
ctx.SendingRequest += (sender, ea) => { ea.RequestHeaders["token"] = "value"; };
ctx.UpdateObject(ctx.Products.First());
ctx.SaveChanges();
Running this using the latest WCF DS 5.0 bits from NuGet works. I also verified with Fiddler that it does send the Content-Type header.
Your request on the other hand doesn't have the Content-Type header. Can you please verify that you're using the latest NuGet packages and that the code you posted above is really what you're testing?

WCF Routing with custom filter: No matching MessageFilter was found for the given message

Im writing a routing service for some WCF services. I want to use a custom message filter that filters out my message.
Here is my custom message filter:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ServiceModel.Dispatcher;
using System.IO;
using System.Xml;
namespace AMA_ISE.Router.MessageFilter
{
/// <summary>
/// This message filter implements the custom filtering for the routing services to re-route the incoming messages
/// correctly.
/// </summary>
public class CustomMessageFilter : System.ServiceModel.Dispatcher.MessageFilter
{
/// <summary>
/// The sales channel for which the filter is configured.
/// </summary>
private string _salesChannel;
/// <summary>
/// The service, for which the filter is configured.
/// </summary>
private string _service;
/// <summary>
/// Initializes a new instance of the <see cref="CustomMessageFilter"/> class.
/// </summary>
/// <param name="messageData">The message data.</param>
public CustomMessageFilter(string messageData)
{
var splitted = messageData.Split("|".ToCharArray());
_salesChannel = splitted[0];
_service = splitted[1];
}
/// <summary>
/// When overridden in a derived class, tests whether a message satisfies the filter criteria. The body cannot be examined.
/// </summary>
/// <param name="message">The <see cref="T:System.ServiceModel.Channels.Message"/> object to test.</param>
/// <returns>
/// true if the <see cref="T:System.ServiceModel.Channels.Message"/> object satisfies the filter criteria; otherwise, false.
/// </returns>
public override bool Match(System.ServiceModel.Channels.Message message)
{
return true;
}
/// <summary>
/// When overridden in a derived class, tests whether a buffered message satisfies the criteria of a filter.
/// </summary>
/// <param name="buffer">The <see cref="T:System.ServiceModel.Channels.MessageBuffer"/> object to test.</param>
/// <returns>
/// true if the <see cref="T:System.ServiceModel.Channels.MessageBuffer"/> object satisfies the filter criteria; otherwise, false.
/// </returns>
public override bool Match(System.ServiceModel.Channels.MessageBuffer buffer)
{
using (var memStream = new MemoryStream())
{
var msgText = GetMessageEnvelope(buffer);
// check sales channel
if (GetValueByTagName(msgText, "SalesChannel") != _salesChannel)
return false;
// check the requested service
var toValue = GetValueByTagName(msgText, "To");
File.AppendAllText("C:\\temp\\filter.txt", toValue + " - " + _service + " - " + toValue.Trim().EndsWith(_service) + "\r\n");
return toValue.Trim().EndsWith(_service);
}
}
/// <summary>
/// Gets the message envelope.
/// </summary>
/// <param name="buffer">The buffer.</param>
/// <returns></returns>
private static string GetMessageEnvelope(System.ServiceModel.Channels.MessageBuffer buffer)
{
using (MemoryStream memStream = new MemoryStream())
{
var msg = buffer.CreateMessage();
XmlDictionaryWriter xmlDictWriter =
XmlDictionaryWriter.CreateTextWriter(memStream);
msg.WriteMessage(xmlDictWriter);
xmlDictWriter.Flush();
memStream.Seek(0, SeekOrigin.Begin);
var reader = new StreamReader(memStream);
var text = reader.ReadToEnd();
return text;
}
}
/// <summary>
/// Gets the name of the value by tag.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="tagName">Name of the tag which content we are searching for.</param>
/// <returns></returns>
private static string GetValueByTagName(string text, string tagName)
{
var txt = text.Substring(text.IndexOf("<" + tagName+" ") + 1);
txt = txt.Substring(txt.IndexOf(">") + 1);
return txt.Substring(0, txt.IndexOf("<"));
}
}
}
As you can see, Im writing some information in a log file located in c:\temp. I expect to match the current filter when Match() returns true; that is not happening. When using this filter, I have the following text file:
http://localhost/AMA-ISE.Router/BookingService.svc - RetrieveService.svc - False
http://localhost/AMA-ISE.Router/BookingService.svc - CancelService.svc - False
http://localhost/AMA-ISE.Router/BookingService.svc - BookingService.svc - True
http://localhost/AMA-ISE.Router/BookingService.svc - AvailService.svc - False
http://localhost/AMA-ISE.Router/BookingService.svc - RebookingService.svc - False
That Looks nice, the BookingService.svc does fits and Match() will give true back. But it doesnt work. When I now use the routing service, I get the following error:
Unhandled Exception: System.AggregateException: One or more errors occurred. ---> System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]: No matching MessageFilter was found for the given Message.
Whats wrong? Have I misunderstood something with the message filters? Please help me!
Michael Baarz
not all XML are formed in the same manner. Sometimes tags containing namespaces directly and sometimes the namespace is prefixed and used. That was why my method has not worked. Here is the working code for a custom filter manager (maybe someone is interested in):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ServiceModel.Dispatcher;
using System.IO;
using System.Xml;
using System.Xml.Linq;
namespace AMA_ISE.Router.MessageFilter
{
/// <summary>
/// This message filter implements the custom filtering for the routing services to re-route the incoming messages
/// correctly.
/// </summary>
public class CustomMessageFilter : System.ServiceModel.Dispatcher.MessageFilter
{
/// <summary>
/// The sales channel for which the filter is configured.
/// </summary>
private string _salesChannel;
/// <summary>
/// The service, for which the filter is configured.
/// </summary>
private string _service;
/// <summary>
/// Initializes a new instance of the <see cref="CustomMessageFilter"/> class.
/// </summary>
/// <param name="messageData">The message data.</param>
public CustomMessageFilter(string messageData)
{
var splitted = messageData.Split("|".ToCharArray());
_salesChannel = splitted[0];
_service = splitted[1];
}
/// <summary>
/// When overridden in a derived class, tests whether a message satisfies the filter criteria. The body cannot be examined.
/// </summary>
/// <param name="message">The <see cref="T:System.ServiceModel.Channels.Message"/> object to test.</param>
/// <returns>
/// true if the <see cref="T:System.ServiceModel.Channels.Message"/> object satisfies the filter criteria; otherwise, false.
/// </returns>
public override bool Match(System.ServiceModel.Channels.Message message)
{
return true;
}
static int counter = 0;
/// <summary>
/// When overridden in a derived class, tests whether a buffered message satisfies the criteria of a filter.
/// </summary>
/// <param name="buffer">The <see cref="T:System.ServiceModel.Channels.MessageBuffer"/> object to test.</param>
/// <returns>
/// true if the <see cref="T:System.ServiceModel.Channels.MessageBuffer"/> object satisfies the filter criteria; otherwise, false.
/// </returns>
public override bool Match(System.ServiceModel.Channels.MessageBuffer buffer)
{
using (var memStream = new MemoryStream())
{
var document = GetMessageEnvelope(buffer);
var salesChannel = GetValueOfNamedNode(document.Root, "SalesChannel");
var toService = GetValueOfNamedNode(document.Root, "To");
return salesChannel == _salesChannel && toService.EndsWith(_service);
}
}
private static string GetValueOfNamedNode(XElement element, string findName)
{
if (element.Name.LocalName == findName)
return element.Value;
foreach (var ele in element.Elements())
{
var foundValue = GetValueOfNamedNode(ele, findName);
if (foundValue != null)
return foundValue;
}
return null;
}
/// <summary>
/// Gets the message envelope.
/// </summary>
/// <param name="buffer">The buffer.</param>
/// <returns></returns>
private static XDocument GetMessageEnvelope(System.ServiceModel.Channels.MessageBuffer buffer)
{
using (MemoryStream memStream = new MemoryStream())
{
var msg = buffer.CreateMessage();
XmlDictionaryWriter xmlDictWriter =
XmlDictionaryWriter.CreateTextWriter(memStream);
msg.WriteMessage(xmlDictWriter);
xmlDictWriter.Flush();
memStream.Seek(0, SeekOrigin.Begin);
var doc = XDocument.Load(memStream);
return doc;
}
}
}
}