I am authoring a RESTful WCF service using .Net 4.0. I want the following two URLS:
/root/document/{ids}?fields={fields}
/root/externaldocument/{ids}?fields={fields}
to map to the same interface member:
Documents GetDocuments(string ids, string fields)
I have tried putting a wildcard into a literal URL segment:
[OperationContract]
[WebGet(UriTemplate = "/root/*document/{ids}?fields={fields}")]
Documents GetDocuments(string ids, string fields)
However, this is not valid and I get the following exception:
The UriTemplate '/root/*document/{ids}?fields={fields}' is not valid; the
wildcard ('*') cannot appear in a variable name or literal... Note that a
wildcard segment, either a literal or a variable, is valid only as the last
path segment in the template
If I wrap the wildcard segment in a template brace:
[OperationContract]
[WebGet(UriTemplate = "/root/{*document}/{ids}?fields={fields}")]
Documents GetDocuments(string ids, string fields)
Then I get an exception because there is no such input parameter in the method arguments:
Operation 'GetDocuments' in contract 'IAPIv2' has a UriTemplate that expects a
parameter named 'DOCUMENTS', but there is no input parameter with that name
on the operation.
My workaround is simply to have two entries, pointing to different methods, and then have the methods call a common implementation:
[OperationContract]
[WebGet(UriTemplate = "/root/document/{ids}?fields={fields}")]
Documents GetDocuments(string ids, string fields)
[OperationContract]
[WebGet(UriTemplate = "/root/externaldocument/{ids}?fields={fields}")]
Documents GetExternalDocuments(string ids, string fields)
But this seems kind of ugly.
I have read the documentation and cannot find this point specifically address. Is there any way I can have a wildcard literal segment in WCF? Or is this not possible in WCF?
As it turned out, the two entry points needed to have slightly different functionality. So I needed to capture which URL was used to enter the method. What I ended up doing was the following:
[OperationContract]
[WebGet(UriTemplate = "/root/{source}ocuments/{ids}?fields={fields}")]
DocumentCollection GetDocumentsById(string source, string ids, string fields);
Both URLs:
/root/document/{ids}?fields={fields}
/root/externaldocument/{ids}?fields={fields}
map to the same URL template, and thus I needed to have only a single entry with a single UriTemplate in my interface.
The "source" input parameter captures either "d" if the second segment is "documents or "externald" if the second segment is "externaldocuments". Thus by inspecting this input parameter, the method can react appropriately, depending upon which URL was used to reach the method.
Note that I could not use the following for the UriTemplate:
[WebGet(UriTemplate = "/root/{source}documents/{ids}?fields={fields}")]
because in this case, the incoming URL
/root/document/{ids}?fields={fields}
would not match the template, even though the template matches if an empty string ("") is used for the source input parameter. Apparently the UriTemplate matching algorithm requires there to be at least one character in a parameter capturing group for there to be a match.