views:

28

answers:

2

I'm trying to create a WCF client to send XML messages to a CGI script. The script functions in a request-response pattern, where the contents of the XML message will determine the action to invoke.

I've starting by creating a service contract to represent the defined set of documents the CGI script will accept:

[ServiceContract]
public interface ICgiService
{
    [OperationContract(Name="request1")]
    [WebInvoke(BodyStyle = WebMessageBodyStyle.Bare)]
    ServiceResponse SubmitRequest(Request1 request);

    [OperationContract(Name="request2")]
    [WebInvoke(BodyStyle = WebMessageBodyStyle.Bare)]
    ServiceResponse SubmitRequest(Request2 request);

    [OperationContract(Name="request3")]
    [WebInvoke(BodyStyle = WebMessageBodyStyle.Bare)]
    ServiceResponse SubmitRequest(Request3 request);
}

I have generated a proxy client from this interface and configured the endpoint with the address to the CGI script and to use HTTP Transport.

When a request is made, the default behaviour of the WCF runtime is to append the operation name to the endpoint address to make the URI http://server/script.cgi/request1.

How do I modify this behaviour so all requests are sent to the endpoint address without any changes to the URI (e.g http://server/script.cgi)?

+1  A: 

You need to supply a URI template to your service operations:

[WebGet(UriTemplate="/")]

(if you're using a GET verb), or

[WebInvoke(UriTemplate="/", Method="POST")]

(if you're using a POST verb).

The UriTemplate can be anything, and it'll be appended to the base URL where the service lives. When you need a GET operation, use [WebGet()] attribute - when you need anything else, use the [WebInvoke(Method="....")] attribute and define what verb you need.

Check out the MSDN Magazine article An Introduction To RESTful Services With WCF for a great intro to WCF and REST.

marc_s
`UriTemplate="/"` still modifies the address by appending "/". Any request other than the original endpoint URI will return 404.
Programming Hero
@Programming Hero: Have you tried `UriTemplate=""` ??
marc_s
Yes, as one of my earlier attempts to make this work. I can't remember the exact behaviour; either I received an exception or it used the operation name instead.
Programming Hero
@Programming Hero: or could you move your *.svc file a level up in the hierarchy of virtual directories, and then use `UriTemplate="script.cgi"` ?? You probably won't be able to use that sample template on three different operations, though ...
marc_s
This is actually the route I'm going for a workaround, so the "operation" becomes the CGI script and the endpoint is the path up to the script. It's not ideal, as it "hard codes" part of the URI to the CGI script in the application source.
Programming Hero
A: 

In the end, what I wanted to achieve doesn't seem simple through out-of-the-box WCF at this time.

To achieve direct control over the addressing, you can implement an IClientMessageFormatter and set the To header value directly, but this is a very heavy interface to be implementing, potentially causing other problems.

To solve my problem I refactored the service contract into:

[ServiceContract]
public interface ICgiService
{
    [OperationContract(Name="script.cgi")]
    [WebInvoke(BodyStyle = WebMessageBodyStyle.Bare)]
    ServiceResponse SubmitRequest(ServiceRequest request);
}

It doesn't allow me to specify the CGI script URL as the full endpoint address, being treated as REST service where the script is a resource.

Additionally, the ServiceRequest class now has to represent all the possible XML structure for each type of request, making the service contracts much harder to understand.

Arguably, this is much closer to the real-world situation: posting various XML documents to a single CGI script that provides a single operation "Do Stuff".

Programming Hero