Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Creating Custom Formatter in ASP.NET Web API for specific formatted response

DZone's Guide to

Creating Custom Formatter in ASP.NET Web API for specific formatted response

·
Free Resource
In this article I will show you how to create a custom formatter in ASP.Net Web API to send formatted response. 

Before diving into deep, let me give you a quick introduction to formatters.  Formatters in ASP .NET Web API plays an important role, when the request comes to the server, depending on the media-type, determines which formatter has to be used to parse the request and assemble the data into appropriate format as response. In ASP.Net Web API when the request is made by the user, Web API framework checks if it is Simple Type or a Complex Type. All types like string, integers, etc. are simple types and by default ASP.Net Web API read those values from URI and uses Model binding. On the other hand for complex types, ASP.Net WEB API framework read it from request body by default (the behavior can be overridden) an depending on the content-type load specific formatter from the list of formatters available in HttpConfiguration list.
When you are creating a custom formatter in ASP.Net Web API, you have two options one is when the request is arrived on server and you want to return the data in specific format or when the request arrives and you want to read/parse the data in specific format.

In this example, we will see how you can return the response in PSV i.e. pipe separated value format.

Create a class that should be derived from BufferedMediaTypeFormatter or MediaTypeFormatter.

The difference is

  • MediaTypeFormatter – This class uses asynchronous read and write methods.
  • BufferedMediaTypeFormatter – This class derives from MediaTypeFormatter but wraps the asynchronous read/write methods inside synchronous methods. Deriving from BufferedMediaTypeFormatter is simpler, because there is no asynchronous code, but it also means the calling thread can block during I/O

In the example, we will use BufferedMediaTypeFormatter

Following are the methods available in BufferedMediaTypeFormatter class

001. // Summary:
002.  
003.  
004.  
005. // Represents a helper class to allow a synchronous formatter on top of the
006.  
007.  
008.  
009. // asynchronous formatter infrastructure.
010.  
011.  
012.  
013. public
014. abstract
015. class
016. BufferedMediaTypeFormatter : MediaTypeFormatter
017.  
018.  
019.  {
020.  
021.  
022.  
023. // Summary:
024.  
025.  
026.  
027. // Initializes a new instance of the System.Net.Http.Formatting.BufferedMediaTypeFormatter
028.  
029.  
030.  
031. // class.
032.  
033.  
034.  
035. protected BufferedMediaTypeFormatter();
036.  
037.  
038.   
039.  
040.  
041. // Summary:
042.  
043.  
044.  
045. // Gets or sets the suggested size of buffer to use with streams in bytes.
046.  
047.  
048.  
049. //
050.  
051.  
052.  
053. // Returns:
054.  
055.  
056.  
057. // The suggested size of buffer to use with streams in bytes.
058.  
059.  
060.  
061. public
062. int BufferSize { get; set; }
063.  
064.  
065.   
066.  
067.  
068. // Summary:
069.  
070.  
071.  
072. // Reads synchronously from the buffered stream.
073.  
074.  
075.  
076. //
077.  
078.  
079.  
080. // Parameters:
081.  
082.  
083.  
084. // type:
085.  
086.  
087.  
088. // The type of the object to deserialize.
089.  
090.  
091.  
092. //
093.  
094.  
095.  
096. // readStream:
097.  
098.  
099.  
100. // The stream from which to read
101.  
102.  
103.  
104. //
105.  
106.  
107.  
108. // content:
109.  
110.  
111.  
112. // The System.Net.Http.HttpContent, if available. Can be null.
113.  
114.  
115.  
116. //
117.  
118.  
119.  
120. // formatterLogger:
121.  
122.  
123.  
124. // The System.Net.Http.Formatting.IFormatterLogger to log events to.
125.  
126.  
127.  
128. //
129.  
130.  
131.  
132. // Returns:
133.  
134.  
135.  
136. // An object of the given type.
137.  
138.  
139.  
140. public
141. virtual
142. object ReadFromStream(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger);
143.  
144.  
145.  
146. //
147.  
148.  
149.  
150. // Summary:
151.  
152.  
153.  
154. // Reads asynchronously from the buffered stream.
155.  
156.  
157.  
158. //
159.  
160.  
161.  
162. // Parameters:
163.  
164.  
165.  
166. // type:
167.  
168.  
169.  
170. // The type of the object to deserialize.
171.  
172.  
173.  
174. //
175.  
176.  
177.  
178. // readStream:
179.  
180.  
181.  
182. // The stream from which to read.
183.  
184.  
185.  
186. //
187.  
188.  
189.  
190. // content:
191.  
192.  
193.  
194. // The System.Net.Http.HttpContent, if available. Can be null.
195.  
196.  
197.  
198. //
199.  
200.  
201.  
202. // formatterLogger:
203.  
204.  
205.  
206. // The System.Net.Http.Formatting.IFormatterLogger to log events to.
207.  
208.  
209.  
210. //
211.  
212.  
213.  
214. // Returns:
215.  
216.  
217.  
218. // A task object representing the asynchronous operation.
219.  
220.  
221.  
222. public
223. override
224. sealed
225. Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger);
226.  
227.  
228.  
229. //
230.  
231.  
232.  
233. // Summary:
234.  
235.  
236.  
237. // Writes synchronously to the buffered stream.
238.  
239.  
240.  
241. //
242.  
243.  
244.  
245. // Parameters:
246.  
247.  
248.  
249. // type:
250.  
251.  
252.  
253. // The type of the object to serialize.
254.  
255.  
256.  
257. //
258.  
259.  
260.  
261. // value:
262.  
263.  
264.  
265. // The object value to write. Can be null.
266.  
267.  
268.  
269. //
270.  
271.  
272.  
273. // writeStream:
274.  
275.  
276.  
277. // The stream to which to write.
278.  
279.  
280.  
281. //
282.  
283.  
284.  
285. // content:
286.  
287.  
288.  
289. // The System.Net.Http.HttpContent, if available. Can be null.
290.  
291.  
292.  
293. public
294. virtual
295. void WriteToStream(Type type, object value, Stream writeStream, HttpContent content);
296.  
297.  
298.  
299. //
300.  
301.  
302.  
303. // Summary:
304.  
305.  
306.  
307. // Writes asynchronously to the buffered stream.
308.  
309.  
310.  
311. //
312.  
313.  
314.  
315. // Parameters:
316.  
317.  
318.  
319. // type:
320.  
321.  
322.  
323. // The type of the object to serialize.
324.  
325.  
326.  
327. //
328.  
329.  
330.  
331. // value:
332.  
333.  
334.  
335. // The object value to write. It may be null.
336.  
337.  
338.  
339. //
340.  
341.  
342.  
343. // writeStream:
344.  
345.  
346.  
347. // The stream to which to write.
348.  
349.  
350.  
351. //
352.  
353.  
354.  
355. // content:
356.  
357.  
358.  
359. // The System.Net.Http.HttpContent, if available. Can be null.
360.  
361.  
362.  
363. //
364.  
365.  
366.  
367. // transportContext:
368.  
369.  
370.  
371. // The transport context.
372.  
373.  
374.  
375. //
376.  
377.  
378.  
379. // Returns:
380.  
381.  
382.  
383. // A task object representing the asynchronous operation.
384.  
385.  
386.  
387. public
388. override
389. sealed
390. Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext);
391.  
392.  
393.  }

When we derive our custom formatter class from BufferedMediaTypeFormatter class we have to override following methods.

01. public
02. override
03. bool CanWriteType(Type type)
04.  
05.  
06.   
07.  
08. public
09. override
10. void WriteToStream(Type type, object value, System.IO.Stream stream, System.Net.Http.HttpContent content)

We can put some logic inside CanWriteType method that checks the type and return true or false. If the resultant value is true the framework calls WriteToStream method otherwise the WriteToStream method won’t be called.

Here is the complete code of custom formatter.

001. public
002. class
003. PsvFormatter : BufferedMediaTypeFormatter
004.  
005.  
006.  {
007.  
008.  
009.  
010. public PsvFormatter()
011.  
012.  
013.  {
014.  
015.  
016.  SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue(“text/pipe”));
017.  
018.  
019.   
020.  
021.  }
022.  
023.  
024.  
025. public
026. override
027. bool CanWriteType(Type type)
028.  
029.  
030.  {
031.  
032.  
033.  
034. if (type == typeof(Session))
035.  
036.  
037.  {
038.  
039.  
040.  
041. return
042. true;
043.  
044.  
045.  }
046.  
047.  
048.  
049. return
050. false;
051.  
052.  
053.  }
054.  
055.  
056.   
057.  
058.  
059. public
060. override
061. void WriteToStream(Type type, object value, System.IO.Stream stream, System.Net.Http.HttpContent content)
062.  
063.  
064.  {
065.  
066.  
067.  
068. using (var writer = new
069. StreamWriter(stream))
070.  
071.  
072.  {
073.  
074.  
075.   
076.  
077.  
078. var singleSession = value as
079. Session;
080.  
081.  
082.  
083. if (singleSession == null)
084.  
085.  
086.  {
087.  
088.  
089.  
090. throw
091. new
092. InvalidOperationException(“Cannot serialize type”);
093.  
094.  
095.  }
096.  
097.  
098. WriteItem(singleSession, writer);
099.  
100.  
101.  }
102.  
103.  
104.  stream.Close();
105.  
106.  
107.   
108.  
109.  }
110.  
111.  
112.   
113.  
114.  
115. private
116. void WriteItem(Session session, StreamWriter writer)
117.  
118.  
119.  {
120.  
121.  
122.  writer.WriteLine(“{0}|{1}|{2}|{3}”, session.Id,
123.  
124.  
125.  session.SessionName, session.SessionDateTime, session.Place);
126.  
127.  
128.  }
129.  
130.  
131.   
132.  
133.  }

In the above code snippet, if you see the constructor I have added the Media type header value in the SupportedMediaTypes list which will be used to pass in the request header information. While invoking WEB API method you can pass text/pipe as ACCEPT header attribute.

In the CanWriteType method I am checking if the type of the object which I am returning is of type Session. In my code example I have a Session whose structure is as follows.

01. public
02. class
03. Session
04.  
05.  
06.  {
07.  
08.  
09.  
10. public
11. int Id { set; get; }
12.  
13.  
14.  
15. public
16. string SessionName { set; get; }
17.  
18.  
19.  
20. public
21. DateTime SessionDateTime { set; get; }
22.  
23.  
24.  
25. public
26. string Place { set; get; }
27.  
28.  
29.  }

In the WriteToStream method I am using the stream writer and writing the object as pipe separated string.

Now in the DataController which is the Web API controller, I have a Get method which fetches the session object from database. After fetching the session object from database, web API framework calls CanWriteType and WriteToStream method and parse the object into Pipe separted string and send that string as the response.

01. public
02. class
03. DataController : ApiController
04.  
05.  
06. {
07.  
08.  
09.  
10. public
11. Session Get()
12.  
13.  
14.  {
15.  
16.  
17.  
18. return repository.All<Session>().Where(i => i.Id == 3).First();
19.  
20.  
21.  }
22.  
23.  
24. }

Using fiddler, I made a request to my data controller and passed text/pipe as ACCEPT attribute this tells the framework that the return response should be text/pipe. Web API framework checks the formatter and uses my custom formatter to serialize the response in pipe separated string.


And the response is here


You can see how easy it is to implement custom formatter in ASP.Net WEB API and in the next series of article I will blog a post about handling custom content type requests in ASP.Net Web API

Topics:

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}