A Look at ASP.NET 5: Part 5 - The API


Posted by Shawn Wildermuth on Sep 27, 2015 on 18:25PM

hddcable

The more I work with ASP.NET 5, the more it looks and feels like the old ASP.NET stack except for the hosting. That’s a good thing in most cases, but writing the API that changes.

After dealing with WCF’s bastardized tried to add REST on top of the SOAP stack, I was elated to be introduced to ASP.NET’s Web API some years back. While it let me develop APIs while thinking about REST in more natural forms, it had the problem of being so separated from the MVC stack that many of the facilities had to be duplicated in both stacks. This cognitive dissonance caused many a developer headaches (same class name but in two different namespaces). When I realized that ASP.NET 5 would be merging the two ideas, I was elated…maybe prematurely.

I’ll explain what I’m doing in a series of blog posts and link them all here as I write them. The plan is to write posts about:

Part 1: Getting Started

Part 2: Startup

Part 3: Using Entity Framework 7

Part 4: ASP.NET MVC 6

Part 5: The API (This Post)

Part 6: Web Tooling with VS2015 (coming soon)

The project will continue to evolve and you can always get the latest version by visiting it on GitHub:

https://github.com/shawnwildermuth/MyCountries

Controllers

In ASP.NET, API Controllers are just controllers. When you define a new API controller, you can simply create your API controllers just like you did for the MVC controllers (same base class):

  public class VisitsController : Controller
  {

In fact,construction is similar too in that you’ll want to use constructor injection like everywhere else in ASP.NET 5:

  public class VisitsController : Controller
  {
    private IMyCountriesRepository _repository;
    public VisitsController(IMyCountriesRepository repository)
    {
      _repository = repository;
    }

In fact, you can just write your action methods like you would controllers that return views but when you’re returning data, just use Json to wrap it:

public async Task<IActionResult> Get()
{
  var username = User.Identity.Name;
  var results = await _repository.GetVisitsByUserNameAsync(username);

  return Json(results);
}

Of note here is also the fact that we can have asynchronous actions as easy as using async/await. But if you’ve done ASP.NET Web API, you might think you’re done. Problem is that there is no built-in mapping between verbs and method names (good and bad, depending who you talk to). Instead, you’ll use the attributes to map an action to a verb (e.g. HttpGet, HttpPost, etc.), but include a route pattern to create the mapping.

Now this attribute + route pattern isn’t new and isn’t limited to API controller (it works in view controllers too), it isn’t the only way to handle routing. You can map the routes in Startup.cs if you like, but I find this easier and more comfortable:

[HttpGet("api/visits")]
public async Task<IActionResult> Get()
{
  var username = User.Identity.Name;
  var results = await _repository.GetVisitsByUserNameAsync(username);

  return Json(results);
}

Lastly, you can prevent duplicating a base route pattern, but adding a Route parameter to the class:

[Route("api/visits")]
public class VisitsController : Controller
{
  private IMyCountriesRepository _repository;
  public VisitsController(IMyCountriesRepository repository)
  {
    _repository = repository;
  }

  [HttpGet("")]
  public async Task<IActionResult> Get()
  {
    var username = User.Identity.Name;
    var results = await _repository.GetVisitsByUserNameAsync(username);

    return Json(results);
  }

Error Handling

A real pet peeve of mine in ASP.NET 5 as it stands (now at Beta 7), is that the helpers that Web API 2 added to more easily return status codes do not exist. Instead, you need to use the HttpStatusCodeResult if you want to return an error condition (and only accepts a numeric version of the status code):

return new HttpStatusCodeResult((int)HttpStatusCode.BadRequest);

It get’s marginally worse when you want to return a successful status code (e.g. 201) and return a valid body:

Response.StatusCode = (int)HttpStatusCode.Created;

var location = string.Concat("/api/visits/", newVisit.Id);
Response.Headers["location"] = location.ToString();

return Json(newVisit);
This works, but feels clunky.

Camel Casing JSON

The last issue is the common case of wanting to camelCase your results. They have simplified this by allowing you to configure this when you add MVC to the Service container (e.g. ConfigureServices in Startup.cs):

// Add MVC services to the services container.
services.AddMvc()
  .AddJsonOptions(opts =>
  {
    opts.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
  });

So it seems that while they’ve merged the API and View controllers into a single, cohesive set of libraries, there were some tradeoffs. Likely once I use it for a big project, I won’t miss any of the Web API’isms, but who knows. You can opt into using Web API with ASP.NET 5 but I prefer to just toss that out with the new stack instead of using the provided shim to make it work, but it’s nice that it exists since migration is a real need.

What do you think?




Application Name WilderBlog Environment Name Production
Application Ver 1.1.0.0 Runtime Framework .NETCoreApp,Version=v1.1
App Path D:\home\site\wwwroot Runtime Version .NET Core 4.6.24628.01
Operating System Microsoft Windows 6.2.9200 Runtime Arch X86