Adding JSON Service Routes (WCF REST Starter Kit P2 vs. WCF Web APIs P3)

Topics: Web Api
Apr 14, 2011 at 10:43 PM

Here are the relevant snippets that I started with using WCF REST Starter Kit Preview 2:

	routes.Add(new ServiceRoute("Users", new WebServiceHost2Factory(), typeof(UserService)));

and

	[WebGet(UriTemplate = "{nameIdentifier}", ResponseFormat = WebMessageFormat.Json)]
	public User GetUser(string nameIdentifier)
	{
		return CreateUser(nameIdentifier);
	}

and here is what I have ported to using WCF Web APIs Preview 3:

	routes.AddServiceRoute<UserService, CustomServiceHostFactory>("Users");

and

	public class CustomServiceHostFactory : WebHttpServiceHostFactory, IConfigurableServiceHostFactory
	{
		protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
		{
			return new WebHttpServiceHost(serviceType, new JsonValueConfiguration(), baseAddresses);
		}

		HttpHostConfiguration IConfigurableServiceHostFactory.Configuration
		{
			get;
			set;
		}
	}

	public class JsonValueConfiguration : HttpHostConfiguration, IProcessorProvider
	{
		public void RegisterRequestProcessorsForOperation(HttpOperationDescription operation, IList<Processor> processors, MediaTypeProcessorMode mode)
		{
			processors.Add(new FormUrlEncodedProcessor(operation, mode));
			processors.Add(new JsonProcessor(operation, mode));
		}

		public void RegisterResponseProcessorsForOperation(HttpOperationDescription operation, IList<Processor> processors, MediaTypeProcessorMode mode)
		{
			processors.Add(new JsonProcessor(operation, mode));
		}
	}

and

	[WebGet(UriTemplate = "{nameIdentifier}")]
	public JsonValue GetUser(string nameIdentifier)
	{
		return JsonValueExtensions.CreateFrom(CreateUser(nameIdentifier));
	}

where CreateUser would actually be a call to an application service (passed on to a repository) in a production system.

Here are my questions:

1. That's quite a bit of code to a.) switch to the cleaner AddServiceRoute and b.) change the signature/attribute of GetUser.  Am I discerning the correct pattern here?

2. Will a class like JsonValueConfiguration come baked into the final bits?  Seems boilerplate to me.

3. Has WebServiceHost2 been deprecated in favor of WebHttpServiceHost?

4. If so, what about the other valuable elements included in WebServiceHost2 like automatic help page, HTTP caching support, new exception handling behavior, and request interception?  Am I mixing apples and oranges here?

Thank you for any feedback.

Apr 14, 2011 at 10:54 PM

I just reviewed the sample solution again and noticed that I can call an overload of AddServiceRoute that eliminates the need for the CustomServiceHostFactory class I added above like so:

	routes.AddServiceRoute<UserService>("Users", new JsonValueConfiguration());

So, if you guys were to give us a default implementation for JsonValueConfiguration, perhaps a new overload could look like:

	routes.AddServiceRoute<UserService, JsonValueConfiguration>("Users");

Which would entirely address questions #1 and #2.

Apr 15, 2011 at 12:27 AM

Hi David,

Have you tried it without creating your own JsonValueConfiguration?

This should work out of the box. If you download the latest drop, you will find a JsonValue sample, which has no custom configuration:

        protected void Application_Start(object sender, EventArgs e)
        {
            RouteTable.Routes.MapServiceRoute<ContactsResource>("contacts");
        }

And

        [WebInvoke(UriTemplate = "", Method = "POST")]
        public JsonValue Post(JsonValue jsonContact) //contact will be passed as JsonValue
        {
            dynamic contact = jsonContact;
            dynamic contactResponse = new JsonObject();
            contactResponse.Name = contact.Name;
            contactResponse.ContactId = nextId++;
            return contactResponse;
        }

Cheers!

Coordinator
Apr 15, 2011 at 1:49 AM

Check our latest bits. JsonValue works out of the box without ANY configuration. The updated JsonValueSample uses no codebase config other than routes

Glenn

Apr 15, 2011 at 2:59 AM
gblock wrote:

Check our latest bits. JsonValue works out of the box without ANY configuration. The updated JsonValueSample uses no codebase config other than routes

Glenn


Indeed.  Awesome.  Thanks, guys.

Apr 21, 2011 at 12:27 PM

This looks a lot simpler! However, I have a question...

In Preview 3 I had a post method like this one:

[WebInvoke(UriTemplate = "", Method = "POST")]
public JsonValue Post(JsonValue contact, HttpRequestMessage request, HttpResponseMessage response)

I use the request to read information from the headers, and the response to craft an error message if any exceptions occur.

However, in the latest preview I'm getting the error below. I'm guessing that I should set the "IsContentParameter" to true for the first parameter... but I'm not clear on how to do that. Any suggestions?

Thanks

WebHost failed to process a request.

Sender Information: System.ServiceModel.ServiceHostingEnvironment+HostingManager/37160817

Exception: System.ServiceModel.ServiceActivationException: The service '/Metanga/RestService/session' cannot be activated due to an exception during compilation. The exception message is: The HttpOperationHandlerFactory is unable to determine the input parameter that should be associated with the request message content for service operation 'Post'. If the operation does not expect content in the request message use the HTTP GET method with the operation. Otherwise, ensure that one input parameter either has it's IsContentParameter property set to 'True' or is a type that is assignable to one of the following: HttpContent, ObjectContent`1, HttpRequestMessage or HttpRequestMessage`1.. ---> System.InvalidOperationException: The HttpOperationHandlerFactory is unable to determine the input parameter that should be associated with the request message content for service operation 'Post'. If the operation does not expect content in the request message use the HTTP GET method with the operation. Otherwise, ensure that one input parameter either has it's IsContentParameter property set to 'True' or is a type that is assignable to one of the following: HttpContent, ObjectContent`1, HttpRequestMessage or HttpRequestMessage`1.

at Microsoft.ApplicationServer.Http.Description.HttpOperationHandlerFactory.ThrowExceptionForUnknownRequestContentParameter(String operationName) in F:\advisorsrepo\wcfhttp\Http\Src\Microsoft.ApplicationServer.Http\Microsoft\ApplicationServer\Http\Description\HttpOperationHandlerFactory.cs:line 374

at Microsoft.ApplicationServer.Http.Description.HttpOperationHandlerFactory.GetRequestContentHandler(HttpOperationDescription operation, String[] uriTemplateParameterNames) in F:\advisorsrepo\wcfhttp\Http\Src\Microsoft.ApplicationServer.Http\Microsoft\ApplicationServer\Http\Description\HttpOperationHandlerFactory.cs:line 292

at Microsoft.ApplicationServer.Http.Description.HttpOperationHandlerFactory.OnCreateRequestHandlers(ServiceEndpoint endpoint, HttpOperationDescription operation) in F:\advisorsrepo\wcfhttp\Http\Src\Microsoft.ApplicationServer.Http\Microsoft\ApplicationServer\Http\Description\HttpOperationHandlerFactory.cs:line 163

at Microsoft.ApplicationServer.Http.Description.DelegateOperationHandlerFactory.OnCreateRequestHandlers(ServiceEndpoint endpoint, HttpOperationDescription operation) in F:\advisorsrepo\wcfhttp\Http\prototypes\Microsoft.ApplicationServer.HttpEnhancements\Microsoft\ApplicationServer\Http\Description\DelegateOperationHandlerFactory.cs:line 40

at Microsoft.ApplicationServer.Http.Description.HttpOperationHandlerFactory.CreateRequestHandlers(ServiceEndpoint endpoint, HttpOperationDescription operation) in F:\advisorsrepo\wcfhttp\Http\Src\Microsoft.ApplicationServer.Http\Microsoft\ApplicationServer\Http\Description\HttpOperationHandlerFactory.cs:line 94

at Microsoft.ApplicationServer.Http.Description.HttpBehavior.OnGetMessageFormatter(ServiceEndpoint endpoint, HttpOperationDescription operation) in F:\advisorsrepo\wcfhttp\Http\Src\Microsoft.ApplicationServer.Http\Microsoft\ApplicationServer\Http\Description\HttpBehavior.cs:line 433

at Microsoft.ApplicationServer.Http.Description.HttpBehavior.OnApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) in F:\advisorsrepo\wcfhttp\Http\Src\Microsoft.ApplicationServer.Http\Microsoft\ApplicationServer\Http\Description\HttpBehavior.cs:line 329

at Microsoft.ApplicationServer.Http.Description.HttpBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) in F:\advisorsrepo\wcfhttp\Http\Src\Microsoft.ApplicationServer.Http\Microsoft\ApplicationServer\Http\Description\HttpBehavior.cs:line 175

at System.ServiceModel.Description.DispatcherBuilder.InitializeServiceHost(ServiceDescription description, ServiceHostBase serviceHost)

at System.ServiceModel.ServiceHostBase.InitializeRuntime()

at System.ServiceModel.ServiceHostBase.OnBeginOpen()

at System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout)

at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)

at System.ServiceModel.Channels.CommunicationObject.Open()

at System.ServiceModel.ServiceHostingEnvironment.HostingManager.ActivateService(String normalizedVirtualPath)

at System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath)

--- End of inner exception stack trace ---

at System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath)

at System.ServiceModel.ServiceHostingEnvironment.EnsureServiceAvailableFast(String relativeVirtualPath)

Process Name: w3wp

Process ID: 6600

Apr 21, 2011 at 1:51 PM

Hey mdesousa,

Not sure as to exactly why you are getting that error, but the signature that you are using is no longer supported in Preview 4.  You can no longer pass in a HttpResponseMessage into an operation.  But now you can create a response and return it.

So, now you would do something like,

        [WebGet(UriTemplate = "foo")]
        public HttpResponseMessage<JsonValue> GetFoo() {
            var jsonValue = JsonValue.Parse("{\"foo\":10}");

            var response = new HttpResponseMessage<JsonValue>(jsonValue);

            return response;
        }

I'm trying to get the POST working with a passed in JsonValue, but something is not working.  I'll let you know if I find out what.

Apr 22, 2011 at 1:54 PM

Hi Darrel,

I actually got the POST to work, by taking an argument of type HttpRequestMessage<JsonValue>.

However, I cannot get the GET to work in the way you described here. On the server side everything seems ok, but on the client side I'm not getting any content in the response. Do I need anything special in the configuration?

Thanks,

Mario

Apr 22, 2011 at 4:22 PM

Mario,

Did you put application/json in the accept header?

Can you show a sample of how you got the POST to work?

Thanks,

Coordinator
Apr 22, 2011 at 4:26 PM
We definitely need to remove that requirement as if you return JsonValue you made it clear what you support.

Sent from my Windows Phone

From: DarrelMiller
Sent: Friday, April 22, 2011 8:22 AM
To: Glenn Block
Subject: Re: Adding JSON Service Routes (WCF REST Starter Kit P2 vs. WCF Web APIs P3) [wcf:253953]

From: DarrelMiller

Mario,

Did you put application/json in the accept header?

Can you show a sample of how you got the POST to work?

Thanks,

Apr 22, 2011 at 9:21 PM

Thanks Darrel!

Adding the application/json to the accept header on the client did the trick.

See below sample code for the POST:

[

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

 

public HttpResponseMessage<JsonValue> Post(HttpRequestMessage<JsonValue> request)

{

var postedJsonMessage = (dynamic)request.Content.ReadAs();

/* Some logic here will use the postJsonMessage and set a result object */

var responseValue = JsonValueExtensions.CreateFrom(result);

return new HttpResponseMessage<JsonValue>(responseValue);

}