I am trying to make an echo web service that replies back with the request content, regardless of what that content is. Just an endpoint listening for anything and spitting it back.
So for example if it is called with "hi", the response content is "hi". If it is called with a multi-part message containing a form data, the data comes back. If it is a JSON message then JSON comes back. This is regardless of what the actual content is or what url parameters are provided. Basically I want it to send the same thing back regardless of the mime type, don't try to interpret it, just spit it back.
I'm starting with the following:
[ServiceContract]
private interface IEchoService
{
[OperationContract]
[WebInvoke]
object Echo(object s);
}
private class EchoService : IEchoService
{
public object Echo(object s)
{
return s;
}
}
WebServiceHost host = new WebServiceHost(typeof(EchoService), new Uri("http://localhost:8002/"));
WebHttpBinding binding = new WebHttpBinding();
ServiceEndpoint ep = host.AddServiceEndpoint(typeof(IEchoService), binding, "echo");
Any ideas how to make this work? This just returns back a HTTP status code of bad request when called.
It looks like the answer is to use the System.ServiceModel.Channels.Message class.
[ServiceContract]
private interface IEchoService
{
[OperationContract]
[WebInvoke]
Message Echo(Message s);
}
private class EchoService : IEchoService
{
public Message Echo(Message s)
{
return s;
}
}
Related
I have a very strange and obscure issue with WCF services that I was hoping to get some insight on:
I am working a WCF service that we are building to replace one that we no longer have source code for. For some reason, in the new WCF service, everything is forced through a single paramater called "request". Using the WCF test client, this is what it looks like
On the "correct" service, this is what it looks like:
Is there any reason why this would be happening? I've defined all of the requests as follows:
[ServiceContract]
public interface IMyService
{
[OperationContract]
string SomeRequest();
}
Which seems correct, but there may be something I've overlooked that is causing this.
In your original WCF service, there is a request function parameter, and it has a definition similar to the following:
[ServiceContract]
public interface IMyService
{
[OperationContract]
Request SomeRequest(Request request);
}
[DataContract]
public class Request
{
string documentId;
[DataMember]
public string DocumentId
{
get { return documentId; }
set { documentId = value; }
}
}
In the new wcf service:
[ServiceContract]
public interface IMyService
{
[OperationContract]
string SomeRequest(string documentId);
}
So this is because the function parameters are different. Originally your parameter was class, but later changed to string, so the display in WCFTestClient is different.
I'm having trouble with implementing URL redirection using message inspectors for my WCF REST service. The idea is to implement AfterReceiveRequest to change the incoming request to effectively do nothing, and then in BeforeSendReply, make call to another URI which provides the actual service. You can think of this scenario as something where we do not want to break clients, so they continue to use old URIs for invocations, but internally we redirect them to a different URI.
For example, this is what my service contract looks like:
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebGet(UriTemplate = "person/{person}")]
string GreetMe(string person);
[OperationContract]
[WebGet(UriTemplate = "donothing")]
string DoNothing();
}
And this is the implementation:
public class Service1 : IService1
{
public string GreetMe(string person)
{
return string.Format("Hi {0}", person);
}
public string DoNothing()
{
return string.Empty;
}
}
Then I'm adding a MessageInspector to endpoint behavior. The AfterReceiveRequest of this inspector is like this:
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
OperationContext operationContext = OperationContext.Current;
if (WebOperationContext.Current != null && WebOperationContext.Current.IncomingRequest.UriTemplateMatch != null)
{
UriBuilder baseUriBuilder = new UriBuilder(WebOperationContext.Current.IncomingRequest.UriTemplateMatch.BaseUri);
UriBuilder requestUriBuilder = new UriBuilder(WebOperationContext.Current.IncomingRequest.UriTemplateMatch.RequestUri);
OperationContext.Current.IncomingMessageProperties["MicrosoftDataServicesRootUri"] = baseUriBuilder.Uri.ToString();
OperationContext.Current.IncomingMessageProperties["MicrosoftDataServicesRequestUri"] = baseUriBuilder.Uri.ToString() + "donothing";
OperationContext.Current.IncomingMessageHeaders.To = new Uri(baseUriBuilder.Uri.ToString() + "donothing");
operationContext.IncomingMessageProperties["Via"] = new Uri(baseUriBuilder.Uri.ToString() + "donothing");
request.Headers.To = new Uri(baseUriBuilder.Uri.ToString() + "donothing");
}
return null;
}
Basically, I want to change all incoming requests to go to DoNothing() operation. However, when I run this, I still see that the request makes it to GreetMe method. I attached the debugger and can see that my code in AfterReceiveRequest is getting executed, but I'm not sure why the URI redirection does not happen.
Any ideas?
I just dived a little deeper into it and discovered new details.
Not the 'UriTemplate' in general causes the 2nd invocation of 'AfterReceiveRequest' but the optional parameter within!
If I call the method by
http:/ /myserver/result/val1
AfterReceiveRequest will be invoked twice.
If I pass all possible parameters like
http:/ /myserver/result/val1/val2/val3
there will be no useless invocation. Is that behavior as intended?
UriTemplate = "result/{para1=null}/{para2=null}/{para3=null}"
--- following the initial post, just for information ---
While implementing a WCF REST Service System I stuck on a problem with the http-headers.
In my ServiceContract there is a method with an UriTemplate definition at the WebGet attribute, so it can be called via
http://server/resource/val1/val2 ...
instead of
http://server/resource?para1=val1¶2=val2 ...
(I need this because of compatibility reasons.)
Also there is a significant value in the http-headers collection, that I need to read. Therefore I implement IDispatchMessageInspector and add this inspector to the EndpointDispatchers MessageInspectors collection. By that the AfterReceiveRequest will be invoked by WCF and I can access WebOperationContext.Current.IncomingRequest.Headers to read the desired Value.
The Problem: WCF solves the UriTemplate-mapping by generating a second request to the destination method, but does not pass the header entries from the original call to the generated second call. So AfterReceiveRequest (and of course BeforeSendReply, too) will be invoked twice, but the header-values from the real client call are only included in the first call.
Also I found no way to correlate the first and the second AfterReceiveRequest call, to implement a "special way" for passing the header-value from the first to the second call.
Is there a way to tell WCF to route the headers to the UriTemplate-redirected second call?
Here are some code fragments to make it clear:
[ServiceContract]
public interface IMyService
{
[WebGet(UriTemplate = "result/{para1=null}/{para2=null}/{para3=null}")]
bool MyMethod(string para1, string para2, string para3);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
[MyServiceInspectorBeavior]
public class MyService : IMyService
{
public bool MyMethod(string para1, string para2, string para3)
{
return DoTheWork();
}
//...
}
public class MyServiceInspectorBeavior : Attribute, IServiceBehavior
{
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (EndpointDispatcher epDispatcher in serviceHostBase.ChannelDispatchers.OfType<ChannelDispatcher>().SelectMany(cDispatcher => cDispatcher.Endpoints))
{
epDispatcher.DispatchRuntime.MessageInspectors.Add(new MyInspector());
}
}
//...
}
public class MyInspector : IDispatchMessageInspector
{
//this is invoked twice for each client request,
//but only at the first call the header is present...
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
WebHeaderCollection webOpContext =
WebOperationContext.Current.IncomingRequest.Headers;
string xForwardedIp = webOpContext["X-FORWARDED-IP"];
WriteLog(xForwardedIp);
return OperationContext.Current.IncomingMessageProperties["ActivityId"];
}
//...
}
I have a WCF service and application. My application occasionally sends some text messages (string[]) to the WCF service, which then parses it.
Now I would like my application to send objects of class MyObject to WCF service. Then the service should have chance also parse (I mean it should has chance to deserialize its data (e.g. myObjectInstance.name, etc.))
How to implement that?
Thanks in advance for help.
Divide your code into (at least) three assemblies:
'Metadata' — contains MyObject and IMyService (which is your service's contract); may not reference any assembly that cannot be deployed at client-side
'Server' — implements IMyService and exposes the WCF service; references 'Metadata'
'Client' — contains the code of the client; reference 'Metadata'
That way you can easily share the 'Metadata' assembly between server and clients. Then it's just a matter of using MyObject as the type of any parameters of any methods in IMyService as you need to.
Yes, you can, but you should use Message. Take a look on the following example
As a result, you code could look like
private static void Main()
{
var createRequest = new CreateClientRequest
{
Email = "emial#emial.com"
};
var response = SoapServiceClient.Post<CreateClientRequest, ClientResponse>(createRequest);
Console.WriteLine("POST: {0}", response);
var updateRequest = new UpdateClientRequest
{
Email = "new#email.com",
Id = response.Id
};
response = SoapServiceClient.Put<UpdateClientRequest, ClientResponse>(updateRequest);
Console.WriteLine("PUT: {0}", response);
var getClient = new GetClientRequest
{
Id = response.Id
};
response = SoapServiceClient.Get<GetClientRequest, ClientResponse>(getClient);
Console.WriteLine("GET: {0}", response);
var deleteRequest = new DeleteClientRequest
{
Id = response.Id
};
SoapServiceClient.Delete(deleteRequest);
Console.ReadKey();
}
with simple ServiceContract
[ServiceContract]
public interface ISoapService
{
[OperationContract(Action = ServiceMetadata.Operations.Process,
ReplyAction = ServiceMetadata.Operations.ProcessResonse)]
Message Process(Message message);
[OperationContract(Action = ServiceMetadata.Operations.ProcessWithoutResonse)]
void ProcessWithoutResonse(Message message);
}
I am implementing WCF service that exposes a method whose [OperationContract] is [XmlSerializerFormat]. I sometimes get request whose body is not valid XML. In such cases I want to log the original body, so I can know why it didn't constitute valid XML. However, I can't get it from the Message object, see my attempts (by implementing IDispatchMessageInspector interface):
public object IDispatchMessageInspector.AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
request.ToString(); // "... Error reading body: System.Xml.XmlException: The data at the root level is invalid. Line 1, position 1. ..."
request.WriteBody(...); // Serialization Exception, also in WriteMessage and other Write* methods
request.GetReaderAtBodyContents(...); // Same
HttpRequestMessageProperty httpRequest = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name]; // no body in httpRequest
}
When looking in watch, request.messageData appears to contain the body - but that's a private member.
How can I get the message buffer without trying to deserialize it?
Yes, you need custom MessageEncoder, unlike message inspectors (IDispatchMessageInspector / IClientMessageInspector) it sees original byte content including any malformed XML data.
However it's not trivial how to implement this approach. You have to wrap a standard textMessageEncoding as custom binding element and adjust config file to use that custom binding.
Also you can see as example how I did it in my project - wrapping textMessageEncoding, logging encoder, custom binding element and config.
For the opposite direction (I am writing a WCF client and the server returns invalid XML), I was able to extract the raw reply message in IClientMessageInspector.AfterReceiveReply by
accessing the internal MessageData property of reply via Reflection, and then
accessing its Buffer property, which is an ArraySegment<byte>.
Something similar might be available for the request message on the server side; so it might be worth examining the request variable in the debugger.
I'm aware that this is not exactly what you are asking for (since you are on the server side, not on the client side), and I'm also aware that using reflection is error-prone and ugly. But since the correct solution is prohibitively complex (see baur's answer for details) and this "raw dump" is usually only required for debugging, I'll share my code anyways, in case it is helpful to someone in the future. It works for me on .NET Framework 4.8.
public void AfterReceiveReply(ref Message reply, object correlationState)
{
object messageData = reply.GetType()
.GetProperty("MessageData",
BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(reply, null);
var buffer = (ArraySegment<byte>)messageData.GetType()
.GetProperty("Buffer")
.GetValue(messageData, null);
byte[] rawData =
buffer.Array.Skip(buffer.Offset).Take(buffer.Count).ToArray();
// ... do something with rawData
}
And here's the full code of the EndpointBehavior:
public class WcfLogger : IEndpointBehavior
{
public byte[] RawLastResponseBytes { get; private set; }
// We don't need these IEndpointBehavior methods
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
public void Validate(ServiceEndpoint endpoint) { }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new MessageCaptureInspector(this));
}
internal class MessageCaptureInspector : IClientMessageInspector
{
private WcfLogger logger;
public MessageCaptureInspector(WcfLogger logger)
{
this.logger = logger;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
// Ugly reflection magic. We need this for the case where
// the reply is not valid XML, and, thus, reply.ToString()
// only contains an error message.
object messageData = reply.GetType()
.GetProperty("MessageData",
BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(reply, null);
var buffer = (ArraySegment<byte>)messageData.GetType()
.GetProperty("Buffer")
.GetValue(messageData, null);
logger.RawLastResponseBytes =
buffer.Array.Skip(buffer.Offset).Take(buffer.Count).ToArray();
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
return null;
}
}
}
Usage:
var logger = new WcfLogger();
myWcfClient.Endpoint.EndpointBehaviors.Add(logger);
try
{
// ... call WCF method that returns invalid XML
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
File.SaveAllBytes(#"C:\temp\raw_response.bin", logger.RawLastResponseBytes);
// Use the exception message and examine raw_response.bin with
// a hex editor to find the problem.
UPDATE
Some others that have run into this issue appear to have created a Customer Message Encoder.
A message encoding binding element serializes an outgoing Message and
passes it to the transport, or receives the serialized form of a
message from the transport and passes it to the protocol layer if
present, or to the application, if not present.