ASP.NET Core Web API custom formatters

Use accept headers to return new formats like CSV from an ASP.NET Core Rest service

postman csv

ASP.NET Core does support out of the box JSON, XML, or plain text formatters based on the ACCEPT Header. In this post, I’ll explain how to specify other formatters and return them based on the ACCEPT Header. As an example, I use a CSV formatter to return a CSV formatter. A controller method returns the format based on the ACCEPT Header which is specified by the client. This can be very useful in cases where you need to export your data to Excel, and the use of more formats makes your application more accessible.

HTTP ACCEPT Header
The Accept request-header field can be used to specify certain media types which are acceptable for the response.  The response media type can differ from the request media type. By sending ACCEPT Headers you let the server know how the client likes the response to be formatted. If the server is able to return the specified formatting. It will respond in that format.

Server side
At the server side, you are able to specify what the response types are you want to deliver to your clients. The default accepted headers are JSON, XML, and plain text. Adding extra response types can be done by configuring a formatter in the startup. When a client sends one or more ACCEPT Headers the server can write the response in the best-matched format.

Add a CSV Formatter
A CSV formatter can be used where you have objects that you want to return in a CSV format to use in for example Excel. Normally the client needs an array of elements. To let the server know that it wants a CSV format the client adds an ACCEPT Header:

Accept:text/csv

Implement a CSV Formatter
A text output formatter has to derive from TextOutputFormatter. By setting the SupportedMediaTypes you tell what types of media you support. When exporting to In this case text/csv. The CanWriteType method does return true if the type that is returned is supported for serialization. The ACCEPT Header must contain text/csv and the return type must be of the type IEnumerable.

/// <summary>
/// Formatter to generate csv
/// </summary>
public class CsvMediaTypeFormatter : TextOutputFormatter
{
/// <summary>
/// CSV Formatter
/// </summary>
public CsvMediaTypeFormatter()
{
SupportedMediaTypes.Add(Microsoft.Net.Http.Headers.MediaTypeHeaderValue.Parse("text/csv"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
/// <summary>
/// Write the response
/// </summary>
/// <param name="context"></param>
/// <param name="selectedEncoding"></param>
/// <returns></returns>
public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
StringBuilder csv = new StringBuilder();
Type type = GetTypeOf(context.Object);
csv.AppendLine(
string.Join<string>(
",", type.GetProperties().Select(x => x.Name)
)
);
foreach (var obj in (IEnumerable<object>)context.Object)
{
var vals = obj.GetType().GetProperties().Select(
pi => new
{
Value = pi.GetValue(obj, null)
}
);
List<string> values = new List<string>();
foreach (var val in vals)
{
if (val.Value != null)
{
var tmpval = val.Value.ToString();
//Check if the value contans a comma and place it in quotes if so
if (tmpval.Contains(","))
tmpval = string.Concat("\"", tmpval, "\"");
//Replace any \r or \n special characters from a new line with a space
tmpval = tmpval.Replace("\r", " ", StringComparison.InvariantCultureIgnoreCase);
tmpval = tmpval.Replace("\n", " ", StringComparison.InvariantCultureIgnoreCase);
values.Add(tmpval);
}
else
{
values.Add(string.Empty);
}
}
csv.AppendLine(string.Join(",", values));
}
return context.HttpContext.Response.WriteAsync(csv.ToString(), selectedEncoding);
}
private static Type GetTypeOf(object obj)
{
Type type = obj.GetType();
Type itemType;
if (type.GetGenericArguments().Length > 0)
{
itemType = type.GetGenericArguments()[0];
}
else
{
itemType = type.GetElementType();
}
return itemType;
}
protected override bool CanWriteType(Type type)
{
return typeof(IEnumerable).IsAssignableFrom(type);
}
}

Configure the Formatter
To use the formatter you have to configure it at startup. This is done by adding the formatter to the output formatters in the AddMvc method:

services.AddMvc(options =>
{
options.OutputFormatters.Add(new CsvMediaTypeFormatter());
}
view raw Startup.cs hosted with ❤ by GitHub

Final thoughts
Adding your own formatters is very easy when you know how it works. In cases you have to export your data to Excel, a CSV formatter makes that a very simple task if you have implemented and registered the formatter.

Photo by: AAron Lee Kuan Leng

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: