04 November 2008
REST in WCF: Varying response content type based on HTTP Request Headers
Damian Mehers made a comment on my blog post from April, but I felt it was worth a full reblog.
Damian's used the same WCF extensibility points I used to produce some boilerplate that varies the response content type from JSON to XML, based on the Accept or Content-Type header of the GET request. He extends WebHttpBehavior to return an IDispatchMessageFormatter that does either JSON or XML.
class WebHttpBehavior2Ex : WebHttpBehavior
{
protected override IDispatchMessageFormatter GetReplyDispatchFormatter(OperationDescription operationDescription,
ServiceEndpoint endpoint)
{
WebGetAttribute webGetAttribute = operationDescription.Behaviors.Find<WebGetAttribute>();
DynamicResponseTypeAttribute mapAcceptedContentTypeToResponseEncodingAttribute =
operationDescription.Behaviors.Find<DynamicResponseTypeAttribute>();
if (webGetAttribute != null && mapAcceptedContentTypeToResponseEncodingAttribute != null) {
// We need two formatters, since we don't know what type we will need until runtime
webGetAttribute.ResponseFormat = WebMessageFormat.Json;
IDispatchMessageFormatter jsonDispatchMessageFormatter =
base.GetReplyDispatchFormatter(operationDescription, endpoint);
webGetAttribute.ResponseFormat = WebMessageFormat.Xml;
IDispatchMessageFormatter xmlDispatchMessageFormatter =
base.GetReplyDispatchFormatter(operationDescription, endpoint);
return new DynamicFormatter() {
jsonDispatchMessageFormatter = jsonDispatchMessageFormatter,
xmlDispatchMessageFormatter = xmlDispatchMessageFormatter };
}
return base.GetReplyDispatchFormatter(operationDescription, endpoint);
}
}
And then in the DynamicFormatter code, he just picks the formatter as appropriate:
class DynamicFormatter : IDispatchMessageFormatter
{
public IDispatchMessageFormatter jsonDispatchMessageFormatter { get; set; }
public IDispatchMessageFormatter xmlDispatchMessageFormatter { get; set; }
public void DeserializeRequest(System.ServiceModel.Channels.Message message, object[] parameters)
{
throw new NotImplementedException();
}
public System.ServiceModel.Channels.Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
Message request = OperationContext.Current.RequestContext.RequestMessage;
// This code is based on ContentTypeBasedDispatch example in WCF REST Starter Kit Samples
// It calls either
HttpRequestMessageProperty prop = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
string accepts = prop.Headers[HttpRequestHeader.Accept];
if (accepts != null)
{
if (accepts.Contains("text/xml") || accepts.Contains("application/xml"))
{
return xmlDispatchMessageFormatter.SerializeReply(messageVersion, parameters, result);
}
else if (accepts.Contains("application/json"))
{
return jsonDispatchMessageFormatter.SerializeReply(messageVersion, parameters, result);
}
}
else
{
string contentType = prop.Headers[HttpRequestHeader.ContentType];
if (contentType != null)
{
if (contentType.Contains("text/xml") || contentType.Contains("application/xml"))
{
return xmlDispatchMessageFormatter.SerializeReply(messageVersion, parameters, result);
}
else if (contentType.Contains("application/json"))
{
return jsonDispatchMessageFormatter.SerializeReply(messageVersion, parameters, result);
}
}
}
return xmlDispatchMessageFormatter.SerializeReply(messageVersion, parameters, result);
}
}
It's pretty nifty and it requires no changes to the app logic. You need to use a custom ServiceHost and use a custom attribute on each Operation. Because it uses the HTTP headers and not the URI itself to determine content-type of the response, I think it has some nice benefits over the approach I described in April.
Damian's got a full VS2008 solution will all the boilerplate code.
check it out.
Comment Notification
If you would like to receive an email when updates are made to this post, please register here
Subscribe to this post's comments using
Comment Policy: No HTML allowed. URIs and line breaks are converted automatically. Your e–mail address will not show up on any public page.