wcf rest message inspector url redirect - c#

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?

Related

WCF service without using proxy in C#

I have got a WCF service from one of my users. I want to check whether the service is working or not without adding any proxy. Is there any way that I can achieve this in my C# code?
You may achieve this by implementing an endpoint at WCF and querying it from the client.
Following is the WCF code I would use.
// Used for communication between WCF and client. Must be implemented both WCF and client sides
public class Response {
public int Id { get; set; }
public string Data { get; set; }
}
// Web Service - Interface
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json,
UriTemplate = "Up")]
string CheckLogin();
}
// Web service - Implementation
public class ServiceImplementation : IService
{
public Response isUp()
{
Response response = new Response();
response.ID = 200;
response.Data = "web service is up";
return response;
}
}
Following is the client method to test if the service is up.
public bool CheckIfUp(string encodedUrl)
{
WebRequest request;
WebResponse ws;
Response response = new Response();
string url = "http://servicePath/isUp"; // your wcf url
try
{
request = WebRequest.Create(url);
ws = request.GetResponse();
return (response.ID == 200);
}
catch (Exception e)
{
Console.Write(e.StackTrace);
}
return false;
}
Hope this helps.
Try appending ?wsdl at the of the URL pointing to the WCF-service.
If your Web service address is
http://services.aonaware.com/DictService/DictService.asmx
you can reach your wsdl file like this:
http://services.aonaware.com/DictService/DictService.asmx?WSDL
The returned WSDL allows you to see all the method the WCF-service provides.

Optional parameter of at UriTemplate causes twice invoces of AfterReceiveRequest - WCF - IDispatchMessageInspector

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&para2=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"];
}
//...
}

WCF ServiceHosts, ServiceEndpoints and Bindings

I currently am running some WCF REST services in a Windows Service (not IIS), using the WebServiceHost. I have a separate interface and class defined for each service, but I'm having some issues understanding how WebServiceHost, ServiceEndpoint and ServiceContracts can be used together to create a selfhosted solution.
The way that I currently set things up is that I create a new WebServiceHost for each class which implements a service and use the name of the class as part of the URI but then define the rest of the URI in the interface.
[ServiceContract]
public interface IEventsService
{
[System.ServiceModel.OperationContract]
[System.ServiceModel.Web.WebGet(UriTemplate = "EventType", ResponseFormat=WebMessageFormat.Json)]
List<EventType> GetEventTypes();
[System.ServiceModel.OperationContract]
[System.ServiceModel.Web.WebGet(UriTemplate = "Event")]
System.IO.Stream GetEventsAsStream();
}
public class EventsService: IEventsService
{
public List<EventType> GetEventTypes() { //code in here }
public System.IO.Stream GetEventsAsStream() { // code in here }
}
The code to create the services looks like this:
Type t = typeof(EventService);
Type interface = typeof(IEventService);
Uri newUri = new Uri(baseUri, "Events");
WebServicesHost host = new WebServiceHost(t, newUri);
Binding binding = New WebHttpBinding();
ServiceEndpoint ep = host.AddServiceEndpoint(interface, binding, newUri);
This works well and the service endpoint for each service is created at an appropriate url.
http://XXX.YYY.ZZZ:portnum/Events/EventType
http://XXX.YYY.ZZZ:portnum/Events/Event
I then repeat for another service interface and service class. I would like to remove the Events in the Url though but if I do that and create multiple WebServiceHosts with the same base URL I get the error:
The ChannelDispatcher at 'http://localhost:8085/' with contract(s) '"IOtherService"' is unable to open its IChannelListener
with the internal Exception of:
"A registration already exists for URI 'http://localhost:8085/'."
I'm trying to understand how the WebServiceHost, ServiceEndpoint and ServiceContract work together to create the ChannelListener.
Do I need a separate WebServiceHost for each class which implements a service? I don't see a way to register multiple types with a single WebServiceHost
Secondly, I'm passing in the interface to the AddServceEndpoint method and I assume that method checks the object for all of the OperationContract members and adds them, the problem is how does the WebServiceHost know which class should map to which interface.
What I would love would be an example of creating a WCF self hosted service which runs multiple services while keeping the interface and the implementation classes separate.
Sounds to me like the problem that you are having is you are trying to register more than one service on the same service URI. This will not work, as you have noticed, each service must have a unique endpoint.
Unique By
IP
Domain
Port Number
Full URL
Examples
http://someserver/foo -> IFoo Service
http://someserver/bar -> IBar Service
http://somedomain -> IFoo Service
http://someotherdomain -> IBar Service
http://somedomain:1 -> IFoo Service
http://somedomain:2 -> IBar Service
You get the idea.
So to directly address your question, if you want more than once service to be at the root url for you site, you will have to put them on different ports. So you could modify your code to be something like
public class PortNumberAttribute : Attribute
{
public int PortNumber { get; set; }
public PortNumberAttribute(int port)
{
PortNumber = port;
}
}
[PortNumber(8085)]
public interface IEventsService
{
//service methods etc
}
string baseUri = "http://foo.com:{0}";
Type iface = typeof(IEventsService);
PortNumberAttribute pNumber = (PortNumberAttribute)iface.GetCustomAttribute(typeof(PortNumberAttribute));
Uri newUri = new Uri(string.Format(baseUri, pNumber.PortNumber));
//create host and all that
I think it might be useful for you to re-think about your URI approach. Uri is a unique resource identifier.
Each your endpoint says that you try to expose outside a different kind of resource it's "Events" and "OtherResource". Thus you need to change your UriTemplates a bit.
I would make it so:
[ServiceContract]
public interface IEventTypesService
{
[OperationContract]
[WebGet(UriTemplate = "", ResponseFormat=WebMessageFormat.Json)]
IList<EventType> GetEventTypes();
[OperationContract]
[WebGet(UriTemplate = "{id}")]
EventType GetEventType(string id);
}
[ServiceContract]
public interface IEventsService
{
[OperationContract]
[WebGet(UriTemplate = "")]
Stream GetEventsAsStream();
[OperationContract]
[WebGet(UriTemplate = "{id}")]
Event GetEvent(string id);
}
public class EventsService: IEventsService, IEventTypesService
{
public IList<EventType> GetEventTypes() { //code in here }
public EventType GetEventType(string id) { //code in here }
public Stream GetEventsAsStream() { // code in here }
public EventType GetEventType(string id) { // code in here }
}
Type t = typeof(EventService);
Type interface1 = typeof(IEventsService);
Type interface2 = typeof(IEventTypesService);
var baseUri = new Uri("http://localhost");
Uri eventsUri= new Uri(baseUri, "Events");
Uri eventTypesUri= new Uri(baseUri, "EventTypes");
WebServicesHost host = new WebServiceHost(t, baseUri);
Binding binding = New WebHttpBinding();
host.AddServiceEndpoint(interface1, binding, eventsUri);
host.AddServiceEndpoint(interface2, binding, eventTypesUri);
And yes, you are right - you have to have different addresses, but it's really different resources. To understand it better you can refer: RESTful API Design, best-practices-for-a-pragmatic-restful-api
To finish, there is a way to use the same address, but the approach a bit weird:
Using the same address
The following solution:
allows a single object to handle a specific endpoint
no part of the path is in the URI template
uses the same port for all of the services
It does requires more than one WebServiceHost - one per object that handles requests. Another difficulty is that adding deeper endpoints (like /events/2014) means they either need to have unique parameters or the URI template must include part of the path, if you go convention over configuration that shouldn't be a problem.
A WebServiceHost can only host one thing (class) but that object can have multiple interfaces to handle multiple different types of requests on different URLs. How can different WebServiceHosts bind to the same domain:port? They can't so I guess WebServiceHost wraps an underlying static object that routes requests to the right object. This doesn't technically answer your question but I think this implementation allows you to do what you want right?
A console app that hosts the web services.
public class Program
{
static void Main (string[] args)
{
var venueHost = new WebServiceHost (typeof (Venues));
venueHost.AddServiceEndpoint (typeof (IVenues), new WebHttpBinding (), "http://localhost:12345/venues");
venueHost.Open ();
var eventHost = new WebServiceHost (typeof (Events));
eventHost.AddServiceEndpoint (typeof (IEvents), new WebHttpBinding (), "http://localhost:12345/events");
eventHost.Open ();
while (true)
{
var k = Console.ReadKey ();
if (k.KeyChar == 'q' || k.KeyChar == 'Q')
break;
}
}
}
The Venues class implements IVenues and handles any requests to http://localhost:12345/venues/
[ServiceContract]
public interface IVenues
{
[WebInvoke (Method = "GET", UriTemplate = "?id={id}")]
string GetVenues (string id);
}
public class Venues : IVenues
{
public string GetVenues (string id)
{
return "This would contain venue data.";
}
}
The Events class implements IEvents and handles any requests to http://localhost:12345/events/
[ServiceContract]
public interface IEvents
{
[WebInvoke (Method = "GET", UriTemplate = "?venue={venue}")]
string GetEvents (string venue);
}
public class Events : IEvents
{
public string GetEvents (string venue)
{
return "This would contain event data.";
}
}
WCF self hosting can be done in many ways like Console application hosting, Windows service hosting, etc.
I had tried to host two services using a single console application. The structure of the services was similar to what you mentioned, that is, separate classes and interfaces for both the services.
You might want to have a look at this link:
Hosting two WCf services using one console app

Generic Echo Web Service

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;
}
}

Access Request Body in a WCF RESTful Service

How do I access the HTTP POST request body in a WCF REST service?
Here is the service definition:
[ServiceContract]
public interface ITestService
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "EntryPoint")]
MyData GetData();
}
Here is the implementation:
public MyData GetData()
{
return new MyData();
}
I though of using the following code to access the HTTP request:
IncomingWebRequestContext context = WebOperationContext.Current.IncomingRequest;
But the IncomingWebRequestContext only gives access to the headers, not the body.
Thanks.
Best way i think doesn't involve WebOperationContext
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "EntryPoint", BodyStyle = WebMessageBodyStyle.Bare)]
MyData GetData(System.IO.Stream pStream);
Use
OperationContext.Current.RequestContext.RequestMessage
Sorry for the late answer but I thought I would add what works with UriTemplate parameters to get the request body.
[ServiceContract]
public class Service
{
[OperationContract]
[WebInvoke(UriTemplate = "{param0}/{param1}", Method = "POST")]
public Stream TestPost(string param0, string param1)
{
string body = Encoding.UTF8.GetString(OperationContext.Current.RequestContext.RequestMessage.GetBody<byte[]>());
return ...;
}
}
body is assigned a string from the raw bytes of the message body.
This code return body text. Need using System, System.Text, System.Reflection, System.ServiceModel
public string GetBody()
{
var requestMessage = OperationContext.Current.RequestContext.RequestMessage;
var messageDataProperty = requestMessage.GetType().GetProperty("MessageData", (BindingFlags)0x1FFFFFF);
var messageData = messageDataProperty.GetValue(requestMessage);
var bufferProperty = messageData.GetType().GetProperty("Buffer");
var buffer = bufferProperty.GetValue(messageData) as ArraySegment<byte>?;
var body = Encoding.UTF8.GetString(buffer.Value.Array);
return body;
}
I was able to solve my issue cobbling together multiple answers on this thread. What I am trying to do is to receive a JSON payload in the body of the POST and not have anything done to it so that I can parse it as I will. This is important to us because the JSON coming in is not a single predetermined thing, but rather one of several possible. Yes, we could add a separate call for each new thing, but we are trying to allow the system to be extensible without code changes.
In previous attempts I have only been able to get this to work if the content type was 'text/plain', but then I'm sitting chewing on my tongue explaining why it can't be sent as 'application/json' when someone wants to call it.
So... from the answers on this page... the following signature:
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "test/", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
void TestCall();
and then getting the JSON from the body like so:
private string GetJSONFromBody()
{
string json = "";
string contentType = WebOperationContext.Current.IncomingRequest.ContentType;
if (contentType.Contains("application/json"))
{
var requestMessage = OperationContext.Current.RequestContext.RequestMessage;
var messageDataProperty = requestMessage.GetType().GetProperty("MessageData", (BindingFlags)0x1FFFFFF);
var messageData = messageDataProperty.GetValue(requestMessage);
var bufferProperty = messageData.GetType().GetProperty("Buffer");
var buffer = bufferProperty.GetValue(messageData) as ArraySegment<byte>?;
json = Encoding.UTF8.GetString(buffer.Value.Array);
}
else if (contentType.Contains("text"))
{
json = Encoding.UTF8.GetString(OperationContext.Current.RequestContext.RequestMessage.GetBody<byte[]>());
}
return json;
}
This way however someone tries to send the JSON it will work, but at last I was able to support 'application/json'. I still needed to support 'text/plain' since there are already apps calling that way.
The above answers helped me come up with this solution. I am receiving json with name/value pairs. {"p1":7514,"p2":3412, "p3":"joe smith" ... }
[OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Bare,
RequestFormat = WebMessageFormat.Json
)]
public Stream getJsonRequest()
{
// Get the raw json POST content. .Net has this in XML string..
string JSONstring = OperationContext.Current.RequestContext.RequestMessage.ToString();
// Parse the XML string into a XML document
XmlDocument doc = new XmlDocument();
doc.LoadXml(JSONstring);
foreach (XmlNode node in doc.DocumentElement.ChildNodes)
{
node.Name // has key
node.InnerText; // has value
It seems that because WCF is designed to be transport protocol-agnostic, a service method doesn't provide access to HTTP-specific information by default. However, I just came across a nice article describing "ASP.Net Compatibility Mode" which essentially allows you to specify that your service is indeed intended to be exposed via HTTP.
Link
Adding the aspNetCompatibilityEnabled configuration to Web.config, combined with the AspNetCompatibilityRequirements attribute to the desired service operations, should do the trick. I'm about to try this myself.
Haw-Bin
My apologies for the previous answer, I stupidly assumed that I had just cast WebOperationContext to get at the OperationContext, unfortunately the real answer is much more ugly.
Let me preface this with, there must be a better way!
First I created my own context object, that could be attached to the existing OperationContext object.
public class TMRequestContext : IExtension<OperationContext> {
private OperationContext _Owner;
public void Attach(OperationContext owner) {
_Owner = owner;
}
public void Detach(OperationContext owner) {
_Owner = null;
}
public static TMRequestContext Current {
get {
if (OperationContext.Current != null) {
return OperationContext.Current.Extensions.Find<TMRequestContext>();
} else {
return null;
}
}
}
}
In order to be able to access this new context object, you need to add it as an extension to the current one. I did that by creating a message inspector class.
public class TMMessageInspector : IDispatchMessageInspector {
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) {
OperationContext.Current.Extensions.Add(new TMRequestContext());
return null;
}
}
In order for the message inspector to work you need to create a new "behaviour". I did this using the following code.
public class TMServerBehavior : IServiceBehavior {
public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) {
//Do nothing
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase) {
foreach (ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers) {
foreach (EndpointDispatcher epDisp in chDisp.Endpoints) {
epDisp.DispatchRuntime.MessageInspectors.Add(new TMMessageInspector());
}
}
}
}
The behaviour you should be able to add in the config file, although I did it by creating a new host and adding the behaviour object manually in the OnOpening method. I ended up using these class for much more than just accessing the OperationContext object. I used them for logging and overriding the error handling and access to the http request object,etc. So, it is not quite as ridiculous solution as it seems. Almost, but not quite!
I really don't remember why I could not just access OperationContext.Current directly. I have a faint recollection that it was always empty and this nasty process was the only way I could get an instance that actually contained valid data.
Here is what I did:
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Web;
using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text;
namespace YourSpaceName
{
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class YourClassName
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "YourMethodName({id})", BodyStyle = WebMessageBodyStyle.Bare)]
public Stream YourMethodName(Stream input, string id)
{
WebOperationContext ctx = WebOperationContext.Current;
ctx.OutgoingResponse.Headers.Add("Content-Type", "application/json");
string response = $#"{{""status"": ""failure"", ""message"": ""Please specify the Id of the vehicle requisition to retrieve."", ""d"":null}}";
try
{
string response = (new StreamReader(input)).ReadToEnd();
}
catch (Exception ecp)
{
response = $#"{{""status"": ""failure"", ""message"": ""{ecp.Message}"", ""d"":null}}";
}
return new MemoryStream(Encoding.UTF8.GetBytes(response));
}
}
}
This code simply reads the input and writes it out.
the body of the POST request is automatically assigned to input irrespect of the variable name. As you can see, you can still have variables in your your UriTemplate.

Categories