Integrating Web API with ASP.NET Razor Web Pages

ASP.NET Web API is the recommended way to build RESTful services over HTTP when using the ASP.NET stack. This article looks at incorporating Web API into an existing Razor Web Pages site, and provides a brief introduction to Web API itself.

What is Web API?

Part of ASP.NET, Web API is a framework for building HTTP-based services. Typically, that means making data available as a service via the HTTP protocol. Services built using Web API conform to the REpresentational State Transfer (REST) architectural pattern. An important element of this is that the service should respond appropriately based on the HTTP verb that was used to make the request (GET, POST, PUT, DELETE etc.). Different verbs are used by the client making the request to express the intent behind the operation. They map to CRUD operations as follows:

HTTP Verb CRUD Operation
POST Create
GET Read
PUT Update
DELETE Delete

Out of the box, Web API delivers data to clients in two formats: JSON (the default) and XML. It is also possible to plug in different formatters to deliver data in other formats. Web API uses content negotiation to decide which format to deliver in response to any particular request. That means that the client, regardless what it is (browser, mobile, web, WPF or other type of app - or anything else capable of generating HTTP requests) can decide what format it wants to work with. This is one of the most powerful aspects of the framework. When combined with the Entity Framework, Web API supports the Open Data Protocol (oData) which offers clients the capability to query data using a range of query operators passed as part of the request. These features are just some of the factors that make Web API a compelling option compared to plain .cshtml files when you want to implement endpoints that just deliver data.

Adding Web API

How you go about adding Web API depends on the version of ASP.NET Web Pages that you are using. If you are working with an existing site that was built using WebMatrix, or you used Visual Studio to create the site from the Razor v 2 template, your site is most likely to be using Web Pages 2, which targets .NET 4. If you built the site using a Razor v3 template in Visual Studio, you are using Web Pages 3, which targets .NET 4.5. The distinction between the two versions is important as it has a bearing on the version of Web API that you can use in your site. Either way, you will install Web API via Nuget, which means you will need to use Visual Studio to include Web API because the Nuget tooling in WebMatrix is not sophisticated enough to allow you to target specific versions. You can use any version of Visual Studio. The Express edition for Web is a good option and is free, as is the Community Edition, which is basically the full Professional edition with some licencing restrictions.

Creating a Solution

Note: This step is only required if you want to add Web API to a Razor Web Pages site that was originally created in WebMatrix.

When you use Nuget from within WebMatrix, package information is stored in the App_Data folder, which is only relevant to web sites. Visual Studio caters for multiple project types - console apps, class libraries, Windows Forms apps and so on, none of which have App_Data folders. Instead, Visual Studio's Nuget integration expects a project or solution folder to exist, and looks to store package information there. So before you try opening your site in Visual Studio, you need to create a solution for it.

  1. Choose the New Project option from the File menu and scroll down to Other Project Types. Select Blank Solution from the Visual Studio Solutions option.

    Web API

  2. Choose a location for the solution and give it a name.

  3. Once the solution opens, right click on the name in Solution Explorer and choose Add » Existing Web Site

    Web API

  4. Navigate to your existing site and add it to the solution.

Installing Web API

You will use the Visual Studio Nuget integration to install a version of Web API that your existing application can support. This walk-through will demonstrate using the Package Manager Console to perform the installation because it provides finer grained control over the version of any package you want to install. This will be necessary particularly if you are adding Web API to an existing Razor Web Pages 2 site.

  1. Navigate to Tools » Library Package Manager » Package Manager Console

    Web API

  2. If your site is a Web Pages 2 site, enter the following into the Package Manager Console:

    Install-Package Microsoft.AspNet.WebApi -Version 4.0.30506

    If your site is built using Web Pages 3, leave off the -version switch:

    Install-Package Microsoft.AspNet.WebApi
  3. If your site is a Web Pages 2 site, add the following to your web.config file just before the closing </configuration> tag:

    <system.webServer>
      <handlers>
        <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
        <remove name="OPTIONSVerbHandler" />
        <remove name="TRACEVerbHandler" />
        <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
      </handlers>
    </system.webServer>

Routes and Web API Controllers

In Web Pages, URLs map to physical files. Unless you use the Web Pages Routing package, a URL must match the path of a file, so a request to www.domain.com/about will expect to find a file called about.cshtml or about.vbhtml in the root folder of the site. The Web API framework (and ASP.NET MVC , for that matter) work in a different way. URLs are mapped to methods on Controller classes. What this means is that instead of the code in a page being executed to generate a response, a method on a controller is executed instead. The mechanism that specifies how URLs are mapped to controller class methods is called Routing. The Routing system needs to be configured, and this is done once for the application. The recommended place for this type of initialisation code is the _AppStart file. If you do not have an AppStart file, create one. Add the following code to it. If you have an existing file, copy the using directives to the top of it and the single MapHttpRoute method call anywhere in your existing code block.

@using System.Web.Routing
@using System.Web.Http
@{
    RouteTable.Routes.MapHttpRoute(
             name: "DefaultApiRoute",
             routeTemplate: "api/{controller}/{id}",
             defaults: new { id = RouteParameter.Optional }
         );
}

The MapHttpRoute call defines a default route. The routeTemplate value specifies that the default URL for all Web API calls must start with api/, and that the next segment of the URL will determine which controller class should be used to serve the response. The specific method on the controller class that should be executed is determined by the HTTP verb used for the request. The last part of the routeTemplate is a parameter called id, which is optional.

Adding a Web API Controller

  1. Right click on the web site in Solution Explorer and choose Add » New Item

    Web API

  2. Choose Web API Controller Class (v1) if you are working with Web Pages 2.

    Web API

    Choose Web API Controller Class (v2) if you are working with Web Pages 3.

    Web API

  3. Name the class TestController.

  4. You will be prompted to save the class file in an App_Code folder. Click Yes to proceed.

    Web API

The default code in the controller looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

public class TestController : ApiController
{
    // GET api/<controller>
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    // GET api/<controller>/5
    public string Get(int id)
    {
        return "value";
    }

    // POST api/<controller>
    public void Post([FromBody]string value)
    {
    }

    // PUT api/<controller>/5
    public void Put(int id, [FromBody]string value)
    {
    }

    // DELETE api/<controller>/5
    public void Delete(int id)
    {
    }
}

The controller class includes two methods named Get with differing signatures. They both respond to requests made with the HTTP GET verb. The first one accepts no parameters and returns an array of strings which will be serialized to the format specified by the caller. The second method requires an integer as a parameter which will be used to filter the results to a single value. Other methods named Post, Put and Delete have no bodies.

Testing the controller

Launch your application in a browser. You can do this from within Visual Studio by pressing Ctrl+F5 to launch without debugging. To test the controller, navigate to http://localhost:nnnn/api/test (wherennnnis the port number your site runs on). Notice the last part of the URL - it matches the controller class's name (TestController), but without the "Controller" part. By default, your browser makes a GET request, and since you have not passed a parameter in the request, the routing system should match your request to the controller's Get method that takes no parameter and returns the string array in some form.

You should try testing the controller in a variety of browsers to explore how the response might differ between them. For example, if you make a request using Chrome or Opera, XML appears in the browser:

Web API

The default response in Internet Explorer is an invitation to open or save a file called test.json:

Web API

At the beginning of this article, I said that Web API delivers JSON by default. So why is that only true for Internet Explorer? Why do some other browsers elicit XML? The answer to that is Content Negotiation. The user agent (browser in this case) specifies the content type(s) it will accept by passing appropriate values in the Accept header of the request. Opera and Chrome both include application/xml within the Accept header by default. Opera even gives application/xml quite a high weighting of 0.9 (highest value is 1.0).

Web API

In the absense of any inclusion of application/json, Web API sees this as a request for data in XML format. Internet Explorer, on the other hand, doesn't include application/xml or application/jsonin the Accept header by default, so Web API defaults to JSON:

Web API

Working with data

In the next section, you will create a Web API controller that generates data and use jQuery to force it to deliver JSON.

  1. Right click on the App_Code folder and choose Add » Class. Name the class Movie.

  2. Replace the template code with the following:

    using System;
    
    public class Movie
    {
        public int ID { get; set; }
        public string Title { get; set; }
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
    }
    
  3. Add a Web API controller of the correct version for your target framework to the App_Code folder. Name the class MoviesController.

  4. Replace the template code with the following:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Web;
    using System.Web.Http;
    
    public class MoviesController : ApiController
    {
        List<Movie> movies = new List<Movie> {
            new Movie{ ID = 1, Title = "Taken", Genre = "Action", ReleaseDate = new DateTime(2008,9,26)},
            new Movie{ ID = 2, Title = "Django Unchained", Genre = "Western", ReleaseDate = new DateTime(2013,1,18)},
            new Movie{ ID = 3, Title = "Cars", Genre = "Animation", ReleaseDate = new DateTime(2006,7,28)},
            new Movie{ ID = 4, Title = "The Hangover", Genre = "Comedy", ReleaseDate = new DateTime(2009,6,12)},
            new Movie{ ID = 5, Title = "The Woman in Black", Genre = "Horror", ReleaseDate = new DateTime(2012,2,10)}
        };
    
        // GET api/<controller>
        public IEnumerable<Movie> Get() {
            return movies;
        }
    
        // GET api/<controller>/5
        public Movie Get(int id) {
            return movies.SingleOrDefault(m => m.ID == id);
        }
    
        // POST api/<controller>
        public void Post([FromBody]Movie movie) {
            movies.Add(movie);
            // Save changes
        }
    
        // PUT api/<controller>/5
        public void Put(int id, [FromBody]Movie movie) {
            Movie existingMovie = movies.SingleOrDefault(m => m.ID == id);
            existingMovie.Title = movie.Title;
            existingMovie.Genre = movie.Genre;
            existingMovie.ReleaseDate = movie.ReleaseDate;
            // Save changes
        }
    
        // DELETE api/<controller>/5
        public void Delete(int id) {
            movies.Remove(movies.SingleOrDefault(m => m.ID == id));
            // Save changes
        }
    }
    
  5. Add a new Empty Page (Razor v n) to your site and name it ApiTestPage.cshtml.

  6. Replace the existing code with the following:

    @{
        Layout = null;
    }
    <!DOCTYPE html>
    
    <html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>My Site's Title</title>
        <script src="//code.jquery.com/jquery.min.js" type="text/javascript"></script>
        <script>
            $(function () {
                $('#btn-all').on('click', function () {
                    $.get('/api/movies', function (data) {
                        $('#result').empty();
                        $.each(data, function (i, v) {
                            $('#result').html($('#result').html() + v.Title + '<br />');
                        });
                    }, 'json');
                });
                $('#btn').on('click', function () {
                    $.get('/api/movies/' + $('#MovieId').val(), function (data) {
                        $('#result').empty()
                        $('#result').html(data.Title + 
                            '<br />' + data.Genre + 
                            '<br />' + data.ReleaseDate);
                    }, 'json');
                });
                $('#btn-delete').on('click', function () {
                    $.ajax({
                        type: 'Delete',
                        url: '/api/movies?id=' + $('#MovieId').val()
                    });
                });
            });
        </script>
    </head>
    <body>
        <button id="btn-all">Get all movies</button> <button id="btn">Get movie</button> <button id="btn-delete">Delete Movie</button>
        @Html.DropDownList("MovieId", Enumerable.Range(1, 5).Select(i => new SelectListItem{Text = i.ToString()}))
        <div id="result"></div>
    </body>
    </html>
    

You have defined a class called Movie that represents a movie object. You have also created a Web API Controller that generates a collection of Movie objects and makes them available individually via one of the Get methods (the one with the parameter called id) and all together via the other Get method. Usually you use a database for managing data. This example doesn't show that. It regenerates the collection of Movie objects on each request. The code in the Post, Put and Delete methods will actually add, update and remove movies from the collection, but without any way to persist these changes, you will need to use the debugger to be able to see the changes.

The UI of the page consists of 3 buttons and a drop down list featuring the numbers 1 to 5. The first button has the value Get All Movies and is wired up via jQuery to a click event handler that makes a request to the Get method that doesn't take a parameter. The jQuery.get() method is used to make an HTTP GET request to /api/movies. The title of each film returned by the API is printed out to the browser. The data type that the request expects in return is json, which is defined in the dataType parameter.

Web API

The next is labelled Get Movie, and its click event handler passes the number selected in the drop down list, which represents the ID of one of the movies. The event handler again requests the API to return json. It prints details for the selected movie to the browser:

Web API

The final button is wired up to a click event handler that makes use of the jQuery,ajax() method, which allows you to specify the HTTP verb used to make the request (as opposed to the get method which always uses GET).

.

$('#btn-delete').on('click', function () {
    $.ajax({
        type: 'Delete',
        url: '/api/movies?id=' + $('#MovieId').val()
    });
});

Using the Debugger

If you have ever used a debugger before, you can skip this section. Otherwise the next few steps will show you how to halt execution of a program that is being run in debug mode, and add a watch to a variable.

  1. Locate the Delete method in the MoviesController and add a breakpoint to it by either clicking on the method name once and hitting F9, or clicking once in the grey margin to the left of the method signature. Either way, a red dot should appear in the grey margin:

    Web API

  2. Start debugging the application by pressing F5.

  3. When the home page appears in the browser, navigate to http://localhost:nnnn/apitestpage, where nnnn represents the port number your application is using.

  4. Click the Delete Movie button, and bring Visual Studio to the front of your screen if it doesn't force its way there. Notice that the red dot now has a yellow arrow on it. This indicates that execution is currently halted at that breakpoint:

    Web API

  5. Right click on the word "movies" and choose Add Watch from the context menu:

    Web API

  6. A Watch Window will open (if it wasn't already open) and includes one entry for the movies variable. The count of the collection is 5.

    Web API

  7. Press F10 once to advance execution by a line. The single line of code in the method body should be highlighted, and the Count value should still be 5.

  8. Press F10 once more to move execution forward another line. Now the closing brace of the method body should be highlighted and the Count value reduced by one to reflect that fact that a movie has been deleted from the collection:

    Web API

Summary

Web API is a powerful framework for building data services over HTTP, and as this article shows, it is easy to add Web API to an existing Razor Web Pages site. If you would like to know more about using Web API, take a look at the ASP.NET Web API tutorials.