Adding Queryable Support to ASP.NET Web API Controller Action Methods

In this article I will show that how we can make our Web API methods queryable so the user can apply query operators and expressions to filter data.

We will use OData library for ASP.NET Web API that have the [Queryable] attribute provided, which facilitates in issuing queries.

1. Create ASP.Net Web API Project

2. Add sample model class which defines properties

3. In this example, I have created a Session class as follows

4. Now add a SessionController which will contain Get, Post, Put, Delete methods for CRUD operations.

5. It will create an empty controller. Now we will specify Get method and return session list as queryable.

6. Now add OData reference from Nuget Package Manager by searching as web api odata and click Install on Microsoft ASP.NET Web API 2 OData

7. Once you install, It will ask you to accept the license agreement, click I Accept and proceed

8. Now add [Queryable] attribute on GetSession method.

That’s it, build the solution and test.

9. Open fiddler and select GET HTTP Method and navigate to the URL i.e. http://[youraddress:port]/api/Session

10. Once you hit the above URI it will show you the complete session list in JSON format as follows

11. Now you can apply query operators on your request and filter data

Following are some query operators you can use to issue queries

$top=n: Returns only the first n entities in an entity set (or in Atom terms, the first n entries in a feed).

$skip=n: Skips the first n entities in an entity set. Using this option lets a client retrieve a series of distinct pages on subsequent requests.

$format: Determines whether data should be returned in JSON or the XML-based Atom/AtomPub format. (The default is Atom/AtomPub.)

$orderby=: Orders results, in ascending or descending order, by the value of one or more properties in those results.

$filter=: Returns only entities that match the specified expression.

$select=: Returns only the specified properties in an entity.

$inlinecount: Returns the server computed count of the number of entries produced by the query request.

Example

To select top 2, my URI would be like http://[youraddress:portNo[/api/Session?$top=2

Result:

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

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

// Summary:


// Represents a helper class to allow a synchronous formatter on top of the


// asynchronous formatter infrastructure.


public
abstract
class
BufferedMediaTypeFormatter : MediaTypeFormatter

{


// Summary:


// Initializes a new instance of the System.Net.Http.Formatting.BufferedMediaTypeFormatter


// class.


protected BufferedMediaTypeFormatter();

 


// Summary:


// Gets or sets the suggested size of buffer to use with streams in bytes.


//


// Returns:


// The suggested size of buffer to use with streams in bytes.


public
int BufferSize { get; set; }

 


// Summary:


// Reads synchronously from the buffered stream.


//


// Parameters:


// type:


// The type of the object to deserialize.


//


// readStream:


// The stream from which to read


//


// content:


// The System.Net.Http.HttpContent, if available. Can be null.


//


// formatterLogger:


// The System.Net.Http.Formatting.IFormatterLogger to log events to.


//


// Returns:


// An object of the given type.


public
virtual
object ReadFromStream(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger);


//


// Summary:


// Reads asynchronously from the buffered stream.


//


// Parameters:


// type:


// The type of the object to deserialize.


//


// readStream:


// The stream from which to read.


//


// content:


// The System.Net.Http.HttpContent, if available. Can be null.


//


// formatterLogger:


// The System.Net.Http.Formatting.IFormatterLogger to log events to.


//


// Returns:


// A task object representing the asynchronous operation.


public
override
sealed
Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger);


//


// Summary:


// Writes synchronously to the buffered stream.


//


// Parameters:


// type:


// The type of the object to serialize.


//


// value:


// The object value to write. Can be null.


//


// writeStream:


// The stream to which to write.


//


// content:


// The System.Net.Http.HttpContent, if available. Can be null.


public
virtual
void WriteToStream(Type type, object value, Stream writeStream, HttpContent content);


//


// Summary:


// Writes asynchronously to the buffered stream.


//


// Parameters:


// type:


// The type of the object to serialize.


//


// value:


// The object value to write. It may be null.


//


// writeStream:


// The stream to which to write.


//


// content:


// The System.Net.Http.HttpContent, if available. Can be null.


//


// transportContext:


// The transport context.


//


// Returns:


// A task object representing the asynchronous operation.


public
override
sealed
Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext);

}

 

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

 

public
override
bool CanWriteType(Type type)

 

public
override
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.


public
class
PsvFormatter : BufferedMediaTypeFormatter

{


public PsvFormatter()

{

SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue(“text/pipe”));

 

}


public
override
bool CanWriteType(Type type)

{


if (type == typeof(Session))

{


return
true;

}


return
false;

}

 


public
override
void WriteToStream(Type type, object value, System.IO.Stream stream, System.Net.Http.HttpContent content)

{


using (var writer = new
StreamWriter(stream))

{

 


var singleSession = value as
Session;


if (singleSession == null)

{


throw
new
InvalidOperationException(“Cannot serialize type”);

}

WriteItem(singleSession, writer);

}

stream.Close();

 

}

 


private
void WriteItem(Session session, StreamWriter writer)

{

writer.WriteLine(“{0}|{1}|{2}|{3}”, session.Id,

session.SessionName, session.SessionDateTime, session.Place);

}

 

}

 

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.

public
class
Session

{


public
int Id { set; get; }


public
string SessionName { set; get; }


public
DateTime SessionDateTime { set; get; }


public
string Place { set; get; }

}

 

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.

public
class
DataController : ApiController

{


public
Session Get()

{


return repository.All<Session>().Where(i => i.Id == 3).First();

}

}

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