BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles C#/Web API Code Generation Patterns for the RAML User

C#/Web API Code Generation Patterns for the RAML User

REST specification languages such as RAML, Swagger, and API Blueprint have become increasingly popular over the last couple of years. But they are primarily a client side tool, used mostly for generating JavaScript/TypeScript files, mocks, and associated client-side unit tests.

Meanwhile our typical .NET backend developer will have experience with C# and SQL, but little interest in the finer details of exposing a REST service. Rather, many are content to just slap some routing attributes on the controller methods so they can get back to their core competencies of data storage and server-to-server communication.

This generally results in countless examples of minor miscommunication. Each time there is a difference in how the UI and server developers think a REST endpoint should be exposed, someone has to update their code. This usually requires a small change, but when repeated over and over again it can be a significant drag on developer productivity.

To combat that, UI developers can turn to specification languages such as RAML to generate the Web API code they want. Then the service developer can concentrate on wiring it up instead of mucking about with routing attributes and HTTP verbs.

This article won’t discuss how to use RAML specifically. Rather, the emphasis will be on what code RAML, or your preferred specification language, needs to produce.

C# Code Generation Concepts

C# 2.0 was designed with code generation in mind. Seeing how common it was to use code generators even in Visual Studio itself, it was given the ability to create partial classes. A partial class contains some, but not necessarily all, of the code that makes up the whole class. This allows you to separate the class over multiple files, some of which are code-generated while others are hand-written. This separation prevents the code generator from wiping out code the developer has manually written.

Unfortunately, this wasn’t enough. Partial classes allow you to add new methods, but not change the behavior of existing ones. For that we had to wait until 2008 and the introduction of partial methods in C# 3.

Superficially, a partial method looks like an abstract method, but this is the wrong analogy. An abstract method has to be implemented somewhere or the code cannot be compiled. Partial methods start out more like empty macros in C++; if the partial method isn’t implemented, the compiler will simply erase the call to the method as if that line of code never existed.

Partial methods have strict requirements. Since the compiler might erase them, they cannot have a return type. Nor can they have an ‘out’ parameter. You can, however, return a value from a partial method using a ‘ref’ parameter. Because ref parameters are required to have a value assigned to them before the partial method is called, the compiler can still satisfy its definite assignment rules if the partial method is erased.

Given this limitation, we’ll be heavily relying on ref parameters.

Pattern for Controller

All REST endpoints must be contained within a controller class. Here is a basic example:

[GeneratedCode("My Tool", "1.0.0.0")]
[RoutePrefix("api/customer")]
public partial class CustomerController : ApiController
{
    //methods go here
}

Web API controllers crated in this fashion typically have two attributes, which are indicated with square brackets. The GeneratedCode attribute indicates the class was created by a tool and developers shouldn’t modify it directly. The optional RoutePrefix attribute is used for attribute-based routing, which we’ll discuss below.

Patterns for Synchronous Methods

The code below shows a basic pattern you can use for your Web API code generation.

[Route("search")]
[HttpGet]
public List<Customer> QueryCustomers(int zipCode, string lastName = "")
{
    Tuple<List<Customer>> result = null;
    QueryCustomers(zipCode, lastName, ref result);
    if (result == null)
        throw new NotImplementedException("RAML defined method wasn’t implemented");
    else
        return result.Item1;
}
partial void QueryCustomers(int zipCode, string lastName, ref Tuple<List<Customer>> result);

Note how the partial method encapsulates the return value in a Tuple object. This allows you to distinguish between a null meaning “the method wasn’t implemented” and a null that means “the method returned a null”.

We use this pattern to ensure the code compiles from day 1. While changes to a RAML defined REST method may break the build, merely adding a new method should never have that effect.

Each REST method should contain at least two attributes. The route attribute is used to determine what the URL looks like. If the class has a RoutePrefix attribute, the route attribute will be appended to it. You can learn more about this topic in the article titled Attribute Routing in ASP.NET Web API 2 By Mike Wasson.

The other attribute indicates which verb should be used. Technically speaking, this could be inferred from name of the method, but using an explicit attribute makes it easier to understand what’s happening at a glance. And because this is being code-generated, there is no cost for the extra verbosity.

You may also want to have your code generator include the Authorize attribute to indicate that a method is only accessible by logged in users.

REST Methods without a return type look slightly different:

[Route("update")]
[HttpPost]
public void UpdateCustomer(Customer customer)
{
    bool wasExecuted = false;
    UpdateCustomer(customer, ref wasExecuted);
    if (!wasExecuted)
        throw new NotImplementedException("RAML defined method wasn’t implemented");
}
partial void UpdateCustomer(Customer customer, ref bool wasExecuted);

In this pattern, the implementer needs to set wasExecuted to true.

Patterns for Asynchronous Methods

These days, synchronous operations are frowned upon whenever there is an asynchronous alternative. While somewhat slower in terms of latency, asynchronous code allows greater throughput for an overall better user experience on heavily used servers.

[Route("search")]
[HttpGet]
public async Task<List<Customer>> QueryCustomersAsync(int zipCode, string lastName = "")
{
    Task<List<Customer>> resultTask = null;
    QueryCustomersAsync(zipCode, lastName, ref resultTask);
    if (resultTask == null)
        throw new NotImplementedException("RAML defined method wasn’t implemented");
    else
        return await resultTask;
}
partial void QueryCustomersAsync(int zipCode, string lastName, ref Task<List<Customer>> resultTask);

The first change you should note is how the return type is wrapped in a Task. This allows the framework to asynchronously wait for the completion of the method.

To unwrap a Task object, you need to use the ‘await’ keyword. This keyword will also allow you to wait for the task to be complete even if it doesn’t return a value. Never read directly from Task.Result in an asynchronous context, as that can result in dead locks.

Again, there is a slightly different pattern for REST methods that don’t return a value.

[Route("update")]
[HttpPost]
public async Task UpdateCustomerAsync(Customer customer)
{
    Task resultTask = null;
    UpdateCustomerAsync(customer, ref resultTask);
    if (resultTask == null)
        throw new NotImplementedException("RAML defined method wasn’t implemented");
    else
        await resultTask;
}
partial void UpdateCustomerAsync(Customer customer, ref Task resultTask);

Supporting Client Disconnects

There is a lot of benefit to cancelling along running operations if the user aborts the request or is otherwise disconnected. To do this in asynchronous code, you need to listen for cancellations using the ClientDisconnectedToken. Here is an example of it in use:

[Route("search")]
[HttpGet]
public async Task<List<Customer>> QueryCustomersCancellableAsync(int zipCode, string lastName = "", CancellationToken cancellationToken = default(CancellationToken))
{
    Task<List<Customer>> resultTask = null;
    QueryCustomersCancellableAsync(zipCode, lastName, cancellationToken, ref resultTask);
    if (resultTask == null)
        throw new NotImplementedException("RAML defined method wasn’t implemented");
    else
        return await resultTask;
}
partial void QueryCustomersCancellableAsync(int zipCode, string lastName, CancellationToken cancellationToken, ref Task<List<Customer>> resultTask);

Note how the cancellation token is marked as optional in the REST method even though the framework will always provide a value. This allows it to occur after any optional parameters.

Controller Base Class

The standard base class for Web API controllers is ApiController. However, the services developer may wish to override this. To allow for overrides, the generated code must omit the base class. But this also makes it necessary for the services developer to specify a base class, without which the APIs won’t be visible.

As a work-around, the code generator can specify a custom base class named by the services developer. This base class would inherit from ApiController and include any shared functionality.

Models

When complex objects are required, the code generator will want to produce them. While you can use plain objects, it is preferable to annotate them with DataContract and DataMember attributes. This allows the service developer to add additional properties that are not exposed to the client simply by not marking a property with DataMemeber.

[DataContract]
public partial class Customer
{
    [DataMember]
    public int CustomerKey { get; set; }
    [DataMember]
    public string CustomerName { get; set; }
}

Model Validation

To enable model validation, add the appropriate attributes to the property in question. Common validation attributes include Required, MaxLength, MinLength, Phone, and EmailAddress. The DataAnnotations Namespace contains the list of built-in validation attributes.

Once the model validation is defined, you still need to enforce it. This is done by adding these two lines at the beginning of the REST method.

if (!ModelState.IsValid)

    throw new HttpResponseException(HttpStatusCode.BadRequest);

Beyond the Basics

Once you have the basic code generator in place, you can look for opportunities to reduce boilerplate code. For example, you could you include username resolution in the generated code, passing the result to the partial method. For example:

[Route("search")]
[HttpGet]
public List<Customer> QueryCustomers(int zipCode, string lastName = "")
{
    var user = [application specific logic]
    Tuple<List<Customer>> result = null;
    QueryCustomers(zipCode, lastName, ref result, user);
    if (result == null)
        throw new NotImplementedException();
    else
        return result.Item1;
}
partial void QueryCustomers(int zipCode, string lastName, ref Tuple<List<Customer>> result, User user);

Another example of boilerplate code reduction would be opening a data-context or other resource and passing it to the partial method. Or you could create log entries that capture request/response info. Pretty much anything that is formulaic and repetitive could be handled in this fashion.

Putting it into Practice

The combination of RAML and C# code generation can significantly reduce friction between the frontend and backend development teams. The secret to making it work is a solid design step where the JavaScript and C# both contribute to and agree on the RAML before either side starts implementing their code. That way when changes are invariably needed down during development, both side will feel comfortable updating the shared RAML.

If you would like to publish an article on RAML, code generation, or .NET in general contact Jonathan Allen at jonathan@infoq.com. And if you are looking for a RAML to C# code generator, take a look at Mulesoft Lab’s RAML Tools for .NET.

About the Author

Jonathan Allen got his start working on MIS projects for a health clinic in the late 90's, bringing them up from Access and Excel to an enterprise solution by degrees. After spending five years writing automated trading systems for the financial sector he has decided to shift into high end user interface development. In his free time he enjoys studying and writing about western martial arts from the 15th thru 17th century.

Rate this Article

Adoption
Style

BT