My previous post about Web API content negotiation showed how to add support for new content formats. As our API may have consumers who prefer query for data using GET requests we have to offer something for these dudes too. In this post I will show you how to make life easier for our Web API consumers by using query string mappings.
I will use media type mapping and formatter from my previous posting Extending content negotiation with new formats. We have to modify code a little bit because we need also single vCard download.
Media type formatter
Here is my media type formatter for vCard. It supports vCard format for single and multiple results. Multiple results are given to write method as IEnumerable<ContactModel>.
public class vCardMediaTypeFormatter : MediaTypeFormatter
{
public vCardMediaTypeFormatter()
{
SupportedMediaTypes.Clear();
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/x-vcard"));
}
protected override bool CanWriteType(Type type)
{
return type == typeof(IEnumerable<ContactModel>) ||
type == typeof(ContactModel);
}
protected override Task OnWriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders,
FormatterContext formatterContext, TransportContext transportContext)
{
return Task.Factory.StartNew(() =>
{
if (type == typeof(ContactModel))
WriteVCard((ContactModel)value, stream);
else
WriteVCard((IEnumerable<ContactModel>)value, stream);
});
}
private void WriteVCard(ContactModel model, Stream stream)
{
var buffer = new StringBuilder();
buffer.AppendLine("BEGIN:VCARD");
buffer.AppendLine("VERSION:2.1");
buffer.AppendFormat("N:{0};{1}\r\n", model.LastName, model.FirstName);
buffer.AppendFormat("FN:{0} {1}\r\n", model.FirstName, model.LastName);
buffer.AppendLine("END:VCARD");
using (var writer = new StreamWriter(stream))
{
writer.Write(buffer);
}
}
private void WriteVCard(IEnumerable<ContactModel> contactModels, Stream stream)
{
var enumerator = contactModels.GetEnumerator();
while (enumerator.MoveNext())
{
WriteVCard(enumerator.Current, stream);
}
}
}
Query string mapping
Now let’s say to our web application that contacts API support vCard also with GET requests. QueryStringMapping class will help us out. We just add correctly initialized query string mapping to vCard media type formatter.
var vcard = new vCardMediaTypeFormatter();
vcard.MediaTypeMappings.Add(new VCardMediaTypeMapping());
vcard.MediaTypeMappings.Add(new QueryStringMapping("format", "vcard", "text/x-vcard"));
GlobalConfiguration.Configuration.Formatters.Add(vcard);
One thing to notice – we made no changes to our API to add support for vCards and query string mappings. Our API provides only functionality and it doesn’t make any decision about output formatting.
Testing query string
Now it’s time to test our query string mapping. We don’t mess with AJAX-requests anymore because we can conveniently use out browser. If I want to download contact that has ID value 11 over Web API I have to use the following query string:
http://my-app/api/contacts/11/?format=vcard
I will also open IE developer tools to monitor traffic. This is what was returned to browser:
But now something weird happens – browser asks if we want to open vCard we just requested. Let’s say yes. Here is what you should see on the screen:
This is good example about how we can use our API in “mixed” mode. We can exchange data between systems but we can also let user to handle some results directly.
Conclusion
Our API can be consumed by different AJAX-clients but now we also made our API easier to use for guys who can easily make GET-requests. By example, PHP guys can use fopen() to make GET-request to our API controller. As we saw we can also use those GET-requests to let end users handle formatted downloads.
View Comments (2)
Hi,
Thanks for this post.
Do you know if it is possible to do the same for xml and json?
I would like to select the returned format with the URL rather than with the header "Accept: application/xml"
Can we achieve Query String Mapping by doing some setting in web.config file?