I am trying to create a simple non-RESTful JSON service using WCF and .NET 4.
I'd like my service to be able to parse a JSON request message with a specific format, something like this:
{ "MethodNameRequest": { "MethodParam1Name": "ParamValue1", "MethodParam2Name": "ParamValue2" } }
The endpoint for this service should reside in a single constant URI ("http://myserver/myservice/") so that all methods could be invoked using a POST request to it.
The problem is that whenever I try to declare two (or more) methods using the same "UriTemplate" and the same HTTP verb "POST" (using WebInvokeAttribute), like this:
[WebInvoke(BodyStyle = WebMessageBodyStyle.Wrapped, ResponseFormat = WebMessageFormat.Json, Method = "POST", UriTemplate = "")]
public string Method1()
{
return "Method1";
}
[WebInvoke(BodyStyle = WebMessageBodyStyle.Wrapped, ResponseFormat = WebMessageFormat.Json, Method = "POST", UriTemplate = "")]
public string Method2()
{
return "Method2";
}
I get the following exception:
In contract '', there are multiple
operations with Method 'POST' and a
UriTemplate that is equivalent to ''.
Each operation requires a unique
combination of UriTemplate and Method
to unambiguously dispatch messages.
Use WebGetAttribute or
WebInvokeAttribute to alter the
UriTemplate and Method values of an
operation.
Any ideas on how I can configure WCF to allow this?
I don't see how WCF could figure out which method to call if it somehow allowed the identical UriTemplate for the different methods. Seems you need to implement logic inside the method to handle content based processing.
Try to ommit the UriTemplate property, use instead the <enableWebScript/> element in web.config. This will allow wcf to automatically handle the requests for you.
Related
I need the base-URI ( or of the resource) which actually triggers this Operation to include it in the response.
[WebInvoke(Method = "GET", UriTemplate = "users/user",BodyStyle=WebMessageBodyStyle.WrappedRequest)]
[OperationContract]
public bool ReadUserAccount(User user,int TaskID, **string baseURL**)
{
//do something - Include further subsequent Resource Urls with help of Base URL
return result;
}
Understood the operation Context of the WCF Framework.
OperationContext.Current.RequestContext.RequestMessage.Headers.To
reads the URL inside a Service Implementation.
I would like to make all the methods I am adding to my WCF service have the following behaviour:
[WebInvoke(Method = "GET", BodyStyle = WebMessageBodyStyle.Wrapped)]
Instead of having to add these attributes to every single [OperationContract] method is there a way I can configure this service wide?
Suppose I implement a WCF REST service with the following contract.
[ServiceContract]
interface INotesService
{
[OperationContract]
[WebInvoke(Method = "GET",
UriTemplate = "notes/{id}")]
Note GetNote(string id);
[OperationContract]
[WebInvoke(Method = "GET",
UriTemplate = "notes")]
IEnumerable<Note> GetNotes();
}
Now, I have an HttpModule in the pipeline to do the authorization, but that code needs to know to which method the request will be dispatched. How do I find the signature of the method that will be invoked by WCF?
I think you should use IDispatchOperationSelector.
See also this article: WCF Extensibility – Operation Selectors
Even though the other answer put me on the right path, it did not really answer my question.
I later found this link which gave me a working solution:
http://tech.blog.oceg.org/2009/04/authorizing-rest-calls-in-wcf.html
However, I found it to be more complex than needed. In .NET 4.5 (which is what I'm on) you can do the following.
I registered my ServiceAuthorizationManager from the ServiceHost.ApplyConfiguration override.
this.Authorization.ServiceAuthorizationManager =
new MyServiceAuthorizationManager();
Then, in its CheckAccessCore method, I called the method below to give me the name of the method to which the request will be dispatched.
private string GetOperationName(OperationContext operationContext)
{
return messageProperties["HttpOperationName"] as string;
}
We have created a WCF webservice. We have created two operation contracts like this:
public interface IRestServiceImpl
{
[OperationContract]
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Xml,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "getID/{id}")]
File getID(string id);
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Xml,
RequestFormat = WebMessageFormat.Xml,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "auth")]
ResponseData Auth(RequestData rData); }
As you can see I have a Get and Post method. This works fine, but if I want to set the response format of my GET service,I have to use XMLSERIALIZERFORMAT tag.If I add this and then try to hit POST service, I get Error:400 "Bad Request Error". How should I set the response of my GET method in the same service.
I have tried adding another service 'Service2.svc' in the project where I have kept only GET methods and 'Service1.svc' has only POST services. In the interface of Service2, i have used XMLSERIALIZERFORMAT but still not able to hit POST service. I think I have to use DATACONTRACTSERIALIZER. But I dont know how to set the attributes in that.
Can anyone please help me out with this?
Thanks
Charan
If you want your GET to send a Response in XML then below setting on your GET method should be enough.
ResponseFormat = WebMessageFormat.Xml
You dont need to use XmlSerializerFormat. Just try to browse to your service in IE and you should see the response from your GET Method would be in XML format.
Regarding getting a Bad Request on your POST try to see on how the request body is being sent to your method. To see your request body try using tools like Fiddler to inspect your request and response. To debug the cause of Bad Request try enabling tracing on your service.
How to enable tracing on your Service
I have a WCF restul service and I want to allow the user to choose what request format they want, i have the decorations
[OperationContract]
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Xml,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "getstreamurl?ch={ch}&format=xml")]
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "getstreamurl?ch={ch}&format=json")]
First of, is there a way to specify the ResponseFormat at runtime and take the format in as an argument to the method? From reading around i dont think so... OK next thing
The above code is ok and works, but im having a problem, i want to be able to specify a default, so when no format arguement is passed then i just default but if i decorate like so
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Xml,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "getstreamurl?ch={ch})]
[OperationContract]
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "getstreamurl?ch={ch}&format=json")]
Where the XML is the default, if i try to call the service method through the browser it tells me that:
UriTemplateTable does not support multiple templates that have equivalent path as template 'getstreamurl?ch={ch}' but have different query strings, where the query strings cannot all be disambiguated via literal values. See the documentation for UriTemplateTable for more detail
They obviously can be distinguished but it seems that WCF is only reading up to the argument and thats it...Any suggestions?
No, I don't think you can do that programmatically at runtime. What you can do of course if to expose two distinct endpoints from your service - one returning XML, another returning JSON, and then programmatically pick which one to call from your client app.
Marc
Update: as Steve Michelotti correctly points out, this automatic switching between JSON and XML can now be achieved in WCF 4.0. WCF 4.0 has an improved REST support which also includes an Format Message Selection feature, based on HTTP accept headers.
For more info on WCF 4.0's new features, see: A Developer's Introduction to WCF 4.0
You can do this if your rest service is configured automatically select response type.
Then on client request simply add needed header Accept: application/json