The Practical Client

Building and Testing a Web API Service

As part of building a client-side application in a test-driven way and using TypeScript, Peter creates a Web API service and writes a test that proves he can access it from JavaScript code -- though there are some "wrinkles" in making this work. Along the way, he has an insight about TypeScript versus other, first-class .NET languages.

In any modern Web application, much depends on the interaction between the client's JavaScript code and the services available back on the server. Microsoft's latest tool for creating server-side services is ASP.NET Web API, so that's what I'll use as the next step in creating my sample application. The Web API isn't JavaScript and, technically, not part of this column. However, the services used by the client are a critical part of my sample application and, more important, a brief look at the service will let me make a few points about TypeScript before I return to the client, later in this article.

A Web API Service
My application's first service is called CustomerManagement. Using the Web API means I need to add a class to my site called CustomerManagementController and have it inherit from the ApiController class, like this:

public class CustomerManagementController : ApiController
{

Because the point of this column is the code in the client, I'll just build a dummy Web API service. My first method, which will be automatically called whenever an HTTP Get method without parameters is executed against my server, will return a List of Customer objects (I'll show a JavaScript call to this service later):

public HttpResponseMessage Get()
{
  List<Customer> custs = new List<Customer>() 
    { new Customer
      {Id= 1, 
      FirstName = "Jan",
      LastName = "Vogel",
      CustomerType = CustomerType.Premium}}, 
        new Customer 
          {Id= 2, 
          FirstName = "Peter",   
          LastName = "Vogel",
          CustomerType = CustomerType.Standard}};

My service method will return an HttpResponseMessage, which allows me to bundle an HTTP response code with the list of Customer objects that I'm returning. I'll use the CreateResponse method the Web API provides on the Request object because it allows the Web API to format the result differently, depending on what the client software has requested (either as XML or JSON, for instance):

HttpResponseMessage hrm =
  this.Request.CreateResponse<
    List<Customer>>(HttpStatusCode.OK, custs);
return hrm;

TypeScript vs. .NET
This leads to the point I want to make about TypeScript. In the first column of this series, I defined a set of classes in TypeScript. However, for the code in my service to work, I'm going to need to define a Customer class in some .NET language and use that class in my Web API controller. That class, and the Enum it depends on, would look something like this in Visual Basic:

Public Class Customer
  public Property Id As Integer
  public Property FirstName As String
  public Property LastName As String
  public Property CustomerType As CustomerType
End Class

Public Enum CustomerType
  Deadbeat,
  Standard,
  Premium
End Enum

Notice that I can write my service in C# but write my classes in Visual Basic and, fundamentally, the .NET Framework doesn't care. But I would've preferred to reference the TypeScript definitions that I created earlier. Unfortunately, the .NET Framework does care about that. Unlike first-class .NET languages such as Visual Basic and C#, TypeScript doesn't compile to the Common Intermediate Language (CIL, formerly known as Microsoft Intermediate Language, or MSIL). This isn't some horrible discovery, of course: TypeScript isn't intended to be a first-class .NET language -- it's just intended to compile to JavaScript. Still, it's too bad.

As a big fan of test-driven development (TDD), I want to test my Web API services. However, I don't need JavaScript for that. Instead, because an ApiController class is just another class, I can write my tests in a first-class .NET language (I've got an upcoming Practical .NET column on how to do that). And, to support accessing my ApiController class from my test project, I'll have to add to my test project a reference to my Web project, something that wasn't necessary when I was testing my TypeScript files.

Once my testing is done, I'll need to set up a route that will convert URL requests to my Web site into calls to my class. Because I'm using an ASP.NET MVC 4 project, I add this code to the WebAPIConfig class in my App_Start folder:

config.Routes.MapHttpRoute(
  name: "CustomerManagementNoParameters",
  routeTemplate: "CustomerManagement",
  defaults: new { controller = "CustomerManagement"}
);

Testing the Client
However, all that server-side testing doesn't mean I don't want to do some client-side testing. In last month's column, "Building and Testing a ViewModel in TypeScript," I designed a view model in TypeScript. I now want to populate that model by calling my Web API service from the client -- and that's something I can only test from JavaScript code. As I discussed in an earlier column, I've added the Chutzpah JavaScript test runner with its Visual Studio adapter to my solution to integrate tests written with QUnit into the Visual Studio testing framework. There is a wrinkle, though.

Until now, I've had a single solution that contains both my Web project and my test project. However, I need to have my Web project running in debug mode before any JavaScript code can call the project's services. To make my service available, I open my solution in a second copy of Visual Studio, set my Web project as the start-up project, specify a page in the project as the start page, and press F5 to get the site hosting my service up and running.

Returning to my Test project, I'm ready to write my first TypeScript test to access a server-side service. This code in my very first test just determines that I can access my service:

test("Get on Customer Management Service", function () {
  $.getJSON("http://localhost:49306/CustomerManagement",
    function (custs) {
      equal(custs.length, 2, 
            "Wrong number of customers retrieved on get all");
    }
    );
});

However, I need three references to make this test work. Two are to the type definition files that define jQuery (to support $.getJSON function) and QUnit (to support the test function) to TypeScript. Those references appear in the file before my test code, and look like this:

/// <reference path="Scripts/typings/qunit/qunit.d.ts" />
/// <reference path="Scripts/typings/jquery/jquery.d.ts" />

Those files (which I got through NuGet, though they come from the DefinitelyTyped project) give me IntelliSense support while entering code, and allow the TypeScript compiler to check my calls to those QUnit and jQuery functions -- it's all "compile time" support.

However, when my test runner (Chutzpah) actually executes my test code, it needs access to the jQuery library itself. Chutzpah also uses <reference> tags to add any JavaScript libraries it needs to its test environment; so technically, I could add another <reference> tag to my test file to support Chutzpah pulling in the real jQuery library. However, because TypeScript also uses <reference> tags, this creates problems: TypeScript will follow those references, include jQuery twice when compiling (once for each reference), and generate a ton of error messages.

Fortunately, the latest version of Chutzpah supports a unique reference tag (<chutzpah_reference>) that the TypeScript compiler ignores. I add that tag to my test project with the previous two references to give Chutzpah the runtime access it needs to jQuery in order to execute my test code:

/// <chutzpah_reference path="Scripts/jquery-2.0.2.js" />

I then run the test from the Visual Studio Test Explorer and get a green light. I've proved that my service can be called from JavaScript. In my next column, I'll start exercising my client-side Model-View-ViewModel (MVVM) and might even (finally) start integrating Knockout to create a real UI.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube