Syncing correlationstate between IClientMessageInspector and IParameterInspector - wcf

I have a wcf client. According to requirements, I need to record some of the metadata in the request (as well as user data which is not included in the request.) Then, if the request is successful I may have to record response metadata and depending on flags, the full soap request.
I am trying to do this the right way (using IParameterInspector to examine the metadata and IClientMessageInspector to get the Soap), but I have no way of correlating the two Interface requests. I am not sure about thread safety here. This is a stripped down version of where I am at...
public class SoapRequestInfo
{
public string UserId { get; set; }
public Guid Key { get; set; }
//would contain a lot more info
}
public class OperationProfilerParameterInspector : IParameterInspector, IClientMessageInspector
{
//before serialization
public object BeforeCall(string operationName, object[] inputs) //IParameterInspector
{
//Add the operation, record some specific inputs to db
return new SoapRequestInfo
{
UserId = "1234",
Key = new Guid()
};
}
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState) //IParameterInspector
{
var info = correlationState as SoapRequestInfo;
//Do some additional logging - easy enough
}
public object BeforeSendRequest(ref Message request, IClientChannel channel) //IClientMessageInspector
{
//want to correlate this with IParameterInspector
return null;
}
public void AfterReceiveReply(ref Message reply, object correlationState) //IClientMessageInspector
{
//May want to log full soap message depending on after call criteria
}
}
I know I can't use a private variable to hold the Guid. I can't use session, because there can be multiple requests in close succession and can't guarantee the response is correct. So how can I uniquely identify the correlationState between the two interfaces?

You can probably use HttpContext.Items to keep your object if your service is running in ASPNET compatibility mode otherwise you can use TLS (Thread Local Storage), put the data in a slot and fetch/clear later.

May eb you can tro to do this little bit diffrent:
From this post passing correlation token to WCF service?:
If you use message version with WS-Addressing you should have it automatically because each messages will contain its autogenerated ID (guid) and each response will contain ID of the request as well. You can access these headers through OperationContext
If using messages is not fit your requirements, probably you can try to put your own id in the header when you send request, and probably update the response with same id.

Related

WCF Custom Client Inspector

An external company has given me a WSDL to consume which has a couple of odd characteristics which I don't want to impact my client code.
Firstly, each OperationContract requires the same username parameter sent over. Instead of setting this each time in my client code I'd like to do this globally.
I believe setting this in a IClientMessageInspector is my best bet, however, with this being a SOAP service I'm a little confused at how to add this into the body.
public class CustomInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
// Add an additional parameter to the SOAP body
return null;
}
}
Secondly, whilst the service does return mapped objects, one of the objects contains an xml document shoved in a CDATA :(
<a:ResponseData>
<![CDATA[ INSERT XML DOCUMENT HERE]]>
</a:ResponseData>
I'm looking to extract the XML out and add it back in without the CDATA and XML declaration so I can add the appropriate properties on my response object. That way it should deserialize like normal(hope that makes sense)
public class CustomInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
// Get the XML from the ResponseData element and remove the CDATA. Add the XML back in (Minus the <xml> declaration)
}
}
Firstly, each OperationContract requires the same username parameter
sent over. Instead of setting this each time in my client code I'd
like to do this globally. I believe setting this in a
IClientMessageInspector is my best bet, however, with this being a
SOAP service I'm a little confused at how to add this into the body.
If you want to add custom message header to the message, you could refer to the following code.
public object BeforeSendRequest(ref Message request, System.ServiceModel.IClientChannel channel)
{
request.Headers.Add(MessageHeader.CreateHeader("username", "", "user"));
request.Headers.Add(MessageHeader.CreateHeader("password", "", "pass"));
return null;
}
Take a look at IClientMessageInspector.
Here are some links may be useful to you.
Adding custom SOAP headers from Silverlight client
https://weblogs.asp.net/paolopia/handling-custom-soap-headers-via-wcf-behaviors
https://social.msdn.microsoft.com/Forums/vstudio/en-US/f1f29779-0121-4499-a2bc-63ffe8025b21/wcf-security-soap-header

What are my options in ServiceStack to assign a unique ID to each request?

I'm building an API with ServiceStack. I'd like each request to have a unique ID so that I can trace it through the system (which is distributed).
I have my contracts assembly containing my API's DTOs, and so I thought the natural place would be to make each Request derive from a base class that had a sealed protected parameterless constructor that assigned a new ID (probably a GUID is fine).
However, it'll be possible to use my API via the clients without necessarily using the contract DTOs assembly - naked, if you will. At that point, the clients can assign whatever IDs they like (since the property will be a string to be accomodating, and I want ID assignment to be quick).
So, this leads me to think that the service should assign request IDs when the requests arrive at the system. So - I'm currently thinking that the best thing to do is have an ID property on each request DTO that is validated to be empty by the API - clients cannot set it. Then, a before-everything filter to assign a value to the DTO property.
Is that sensible?
Is there a more elegant way to do it (that still works against naked clients?)?
Using a global request filter would work, you can do something like:
public class IRequiresUniqueId
{
public Guid UniqueId { get; set; }
}
And then mark all request DTOs you would like to have a Unique Id by implementing the above interface:
public MyRequest : IRequiresUniqueId
{
public Guid UniqueId { get; set; }
}
Then you can use a Global Request Filter to set all request DTOs that have them:
this.RequestFilters.Add((httpReq, httpResp, requestDto) =>
{
var requiresUniqueId = requestDto as IRequiresUniqueId;
requiresUniqueId.UniqueId = Guid.NewGuid();
});

Send custom data as object using WCF

In my latest project, I wish to send custom data as an object using WCF. Reason for this is that I won't have to update each client when a new data class is introduced.
However, when I try to send this data, it never arrives at the client side.
To give a short example:
A custom class:
[DataContract]
public class MyData
{
[DataMember]
public string Name { get ;set; }
[DataMember]
public id Value { get; set; }
public MyData(string name, id value)
{
this.Name = name;
this.Value = value;
}
}
When I want to send this to the client, I use:
object obj = new MyData("test",1);
service.SendDataToClient(obj);
The client never receives this event from the service when I send it as object. However, when I send it as MyData instead of object, it works as it should. How can I send this as object?
If you want to send custom data the easy way is using XElement instead of object. Another approach is defining all possible transfered types by ServicKnownTypeAttribute or creating generic resolver (in such case you must share contract assembly between client and service). Check this great article.

Will WCF use the same ParameterInspector instance to handle BeforeCall and AfterCall?

If I create a class that implements IParameterInspector, and insert it into the WCF pipline using a custom ServiceBehavior, will the same instance of the class be used when invoking BeforeCall and AfterCall? In other words, can I establish state about the current invocation during BeforeCall that I can access in AfterCall, and be sure that the response will come to the same instance?
Note _stateValue in the sample code below? Can I depend on a mechanism like this?
class OperationParameterInspector : IParameterInspector
{
public int _stateValue;
public object BeforeCall(string operationName, object[] inputs)
{
_stateValue = (int) inputs[0];
return null;
}
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
int originalInput = _stateValue;
return;
}
}
Passing state related to a particular call is the purpose of the return value from BeforeCall and the correlationState argument of AfterCall. The WCF infrastructure ensures that whatever object you return from BeforeCall is then passed into AfterCall via the correlationState, after the operation has completed.
As your subsequent comment suggests, the problem with using instance state in the inspector object is that instances may be shared between concurrent requests in some scenarios. However, I don't think there are any scenarios where a single operation request would be served by different parameter inspector objects in BeforeCall and AfterCall.

WCF 4: Passing Empty parameters on a GET request

I'm creating an API which will just use a get request to return some search results from the database, I'm trying to make it so that optional parameters can be passed (easy with WCF) but also so that if parameters are specfied in the query string as long as they are empty they will be ignored by the service.
However if you have the a query string with empty parameters it will return a bad request (400) by the server e.g.
Using a end-user point of your choice pass the following querystring
http://www.exampleservice.com/basic/?apiKey=1234&noOfResults=3&maxSalary=&minSalary=&ouId=0&keywords=Web+Developer
Note that maxSalary and minSalary are not passing values
You then have the following WCF service:
[OperationContract]
[WebGet(UriTemplate = "basic/?apiKey={apiKey}&noOfResults={noOfResults}&maxSalary={maxSalary}&minSalary={minSalary}&ouId={ouId}&keywords={keywords}", BodyStyle = WebMessageBodyStyle.Bare)]
public List<SearchResultsDto> BasicSearch(string keywords, string apiKey, int noOfResults, int maxSalary, int minSalary, int ouId)
{
//Do some service stuff
}
This will cause a 400 error, please can someone explain how you pass empty parameters across to a WCF service or is this just not possible?
Currently passing null or an empty parameter is not supported in WCF, the main solution to this problem is to override the querystringconverter which handles the url as it comes through the pipe but before it reaches the operation contract.
An excellent example of implmenting an extension of the querystringconverter is found here:
In the WCF web programming model, how can one write an operation contract with an array of query string parameters (i.e. with the same name)?
HOWEVER
sadly there is a bug in WCF 4 where you cannot override the querystringconverter, this has been addressed by Microsoft and will be fixed in the SP1 release coming this year.
Until then there is no clean way to deal with this situation other than to handle the exception and return a status code of 400 (bad request) - good documentation of the api should handle this in the interim.
Is it just the integers giving you trouble? Maybe you can try making them nullable?
int? MaxSalary
hope this helps
You could send in "-1", and treat that in your business logic as not sent.
It can be handled in multiple ways. Since you are talking about a REST service that can have optional parameters, my suggestion will be do the something like this.
Create a DataObject that will be accepeted as parameter to this method.
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebGet(RequestFormat=WebMessageFormat.Json)]
RequestObject BasicSearch(RequestObject apiKey);
}
public class Service1 : IService1
{
public RequestObject BasicSearch(RequestObject obj)
{
//Do some service stuff
return obj;
}
}
[DataContract]
public class RequestObject
{
[DataMember]
public string Keywords {get; set;}
[DataMember]
public string ApiKey {get; set;}
[DataMember]
public int NoOfResults { get; set; }
}
Advantages (am going to be short, ping me back for details)
No change in service signature
contract does not change
you will get the flexibility of have
null parameters
you can always extend the number of
parameters without any impact to
existing services
below is the sample input and output from fiddler
note: in the request part i havent passed anything to NumberOfResults intentionally to prove