Extensible point of MVC: Custom response type in MVC and WebAPI
We know that MVC has many extensible points where we can hook up our custom code and change the normal behavior of a framework. Those implementations are really helpful when the default framework is not able to meet our requirements. In this article, we will implement a custom return type in MVC and WebAPI. Those implementations could be helpful when we want to modify return type based on an application’s demand.
Custom return type in MVC
We know that ActionResult
is a base class of all return types in MVC. Return types like ViewResult
, JsonResult
, EmptyResutl
etc. are derived from ActionResult
. We can derive our return type from the ActionResult
class as well.
Let’s think of some use cases where we want to limit the return items in a response message. For example, we have lists of objects and on demand we will decide how many objects we will return. Though there are many ways to do this (like result filter), OData
are there to implement such scenarios but we can use a custom response type to implement as well.
Here we have LimitResult
class which is derived from ActionResult
. Within constructor we care about filtering out items based on value of “Count
”.
public class LimitResult : ActionResult
{
Dictionary<int, Person> _disc = new Dictionary<int, Person>();
public LimitResult(List<Person> list, int Count)
{
foreach (var item in list.Take(Count))
{
_disc.Add(item.Id, item);
}
}
public override void ExecuteResult(ControllerContext context)
{
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = "application/json";
response.Write(JsonConvert.SerializeObject( _disc));
}
}
Now we will use the custom class as a response from action.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
public class HomeController : Controller
{
public ActionResult Index()
{
List<Person> person = new List<Person>();
person.Add(new Person { Id = 1, Name = "Sourav" });
person.Add(new Person { Id = 2, Name = "Kayal" });
return new LimitResult(person , 1);
}
}
We are seeing that only one item has returned because we set that only one result is allowed in response.
Custom Result in WebAPI 2
We know that Web API 2 supports IHttpActionResult
as type. Though we can use HttpResponse
as return type. In this example, we will implement IHttpActionResult
in our custom response class. Here we have wrap ForbiddenResult
class and InternalServerErrorResult
class by CustomResultApiController
class. So the CustomResultApiController
looks like a package of custom response type.
public abstract class CustomResultApiController : ApiController
{
public class ForbiddenResult : IHttpActionResult
{
private readonly HttpRequestMessage _request;
private readonly string _reason;
public ForbiddenResult(HttpRequestMessage request, string reason)
{
_request = request;
_reason = reason;
}
public ForbiddenResult(HttpRequestMessage request)
{
_request = request;
_reason = "Forbidden";
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = _request.CreateResponse(HttpStatusCode.Forbidden, _reason);
return Task.FromResult(response);
}
}
public class InternalServerErrorResult : IHttpActionResult
{
private readonly HttpRequestMessage _request;
private readonly string _errormessage;
public InternalServerErrorResult(HttpRequestMessage request, string error)
{
_request = request;
_errormessage = error;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = _request.CreateResponse(HttpStatusCode.InternalServerError, _errormessage);
return Task.FromResult(response);
}
}
}
The IHttpActionResult
interface contains the ExecuteAsync()
function and we have implemented in both classes. Here is how we can use our custom classes as a response object.
public class HomeController : CustomResultApiController
{
[System.Web.Http.HttpGet]
public IHttpActionResult Forbidded()
{
return new ForbiddenResult(Request, "My Custom Reason");
}
}
We are seeing that the response message is embedded in the body of the response object.
In same way we can invoke InternalServerErrorResult
.
[System.Web.Http.HttpGet]
public IHttpActionResult InternalError()
{
return new InternalServerErrorResult(Request, "My Custom Error message");
}
Conclusion
Custom response type is really helpful when you want to override default behavior of frameworks return type.