Skip to content
Diego Fernandez edited this page Mar 6, 2014 · 2 revisions

Before we kick-off and talk about resource handling in Simple.Web let's be clear what a "resource" is.

"A resource can be essentially any coherent and meaningful concept that may be addressed. A representation of a resource is typically a document that captures the current or intended state of a resource." -- Wikipedia

A resource is not a URI, which is just a locator for a resource. Neither is a resource a representation, such as JSON, XML or HTML. And a resource is not the verb used to interact with your application, which are HTTP definitions. A resource is state and methods in your applications' domain that you have chosen to expose.

In summary; you design the resource and Simple.Web will handle the rest.

Identifying a resource

Simple.Web uses the class-level attribute UriTemplate to locate and route an incoming request. You are able to apply the attribute any number of times, and you can optionally inherit from a base class.

// Customers.cs
// http://localhost:3333/customers
namespace ResourceHandling
{
  using System;

  using Simple.Web;

  [UriTemplate("/customers", inheritFromBaseClass: false)]
  public class Customers
  {
  }
}

Querying

What happen's if we need to query our resource? We have two options;

  • Path segments
  • Querystring
// Path Segments
// http://localhost:3333/customer/e430ed07-6c4c-4cb8-ba8b-65523797530a
namespace ResourceHandling
{
  using System;

  using Simple.Web;

  [UriTemplate("/customer/{CustomerUid}")]
  public class Customer
  {
      public Guid CustomerUid { get; set; }
  }
}
// QueryString
// http://localhost:3333/customers?forename=john&surname=brown
namespace ResourceHandling
{
  using System;

  using Simple.Web;

  [UriTemplate("/customers")]
  public class Customers
  {
      public string Forename { get; set; }

      public string Surname { get; set; }
  }
}

Methods

We now have a means for Simple.Web to route an incoming request to the correct resource, but we need to define the appropriate HTTP method for the operation we are supporting on this resource.

Sidenote: Using the correct HTTP method is a core principal of a ReST API, it should reflect the nature of the operation against the resource in conjunction with it's Uniform Resource Identifier (URI).

Simple.Web supports the following HTTP definitions;

  • Delete
  • Get
  • Head
  • Patch
  • Post
  • Put

Note: Simple.Web favours composition over inheritence as a core principal of it's design. To infer usage, and take advantage of strong-typing, Simple.Web provides a series of interfaces that are used to automagically wire-up your application at runtime. This also has the advantage of not polluting your unit tests with inherited behaviour, thus enforcing the unit test boundary.

I am going to use IGet to retrieve a customer and IPost to create a new customer. I am also organising my classes into namespaces that represent the 'customer' resource.

// [GET] GetEndpoint.cs
// http://localhost:3333/customer/e430ed07-6c4c-4cb8-ba8b-65523797530a
namespace ResourceHandling.Customer
{
  using System;
  using Simple.Web;

  [UriTemplate("/customer/{CustomerUid}")]
  public class GetEndpoint : IGet
  {
    public Guid CustomerUid { get; set; }

    public Status Get()
    {
      // Locate the customer by this.CustomerUid
      // and return the appropriate status code
      throw new NotImplementedException();
    }
  }
}
// [POST] PostEndpoint.cs
namespace ResourceHandling.Customer
{
  using System;

  using Simple.Web;

  [UriTemplate("/customer")]
  public class PostEndpoint : IPost
  {
    public Status Post()
    {
      // Need to create a customer and return appropriate
      // status code, although I don't yet have the data!
      throw new NotImplementedException();
    }
  }
}

Sidenote: Async is fully supported throughout Simple.Web, simply append "Async" to the method interface, for example IGetAsync, IPostAsync, etc.

State

You will realise we are building the various elements that sit around our resource but that we are missing the state that will be (a) represented back to the client, and (b) received into our application. As before Simple.Web uses interfaces to infer usage, this time "Behaviors" IOutput<> and IInput<>.

// CustomerModel.cs
namespace ResourceHandling.Customer
{
  using System;

  public class CustomerModel
  {
    public Guid CustomerUid { get; set; }

    public string Forename { get; set; }

    public string Surname { get; set; }
  }
}
// [GET] GetEndpoint.cs
// http://localhost:3333/customer/e430ed07-6c4c-4cb8-ba8b-65523797530a
namespace ResourceHandling.Customer
{
  using System;

  using Simple.Web;
  using Simple.Web.Behaviors;

  [UriTemplate("/customer/{CustomerUid}")]
  public class GetEndpoint : IGet, IOutput<CustomerModel>
  {
    private ICustomerQuery customerQuery;

    public GetEndpoint(ICustomerQuery customerQuery)
    {
      this.queryCustomer = queryCustomer;
    }

    public Guid CustomerUid { get; set; }

    public CustomerModel Customer { get; set; }

    public Status Get()
    {
      // Locate the customer by this.CustomerUid
      this.Customer = this.customerQuery.Execute(this.CustomerUid);

      // We still need to return a status code
      throw new NotImplementedException();
    }
  }
}
// [POST] PostEndpoint.cs
namespace ResourceHandling.Customer
{
  using System;

  using Simple.Web;
  using Simple.Web.Behaviors;

  [UriTemplate("/customer")]
  public class PostEndpoint : IPost, IInput<CustomerModel>
  {
    private ICustomerCommand customerCommand;

    public PostEndpoint(ICustomerCommand customerCommand)
    {
      this.customerCommand = customerCommand;
    }

    public CustomerModel Customer { get; set; }

    public Status Post()
    {
      // Need to create this.CustomerUid
      this.customerCommand.Execute(this.Customer);

      // We still need to return an **appropriate** status code
      throw new NotImplementedException();
    }
  }
}

Status

We need to send back an HTTP status code. Simple.Web provides an implicit cast from Status to Int32 so we can simply return the status code (e.g. 200 = OK), alternatively use the static helper fields.

// [GET] GetEndpoint.cs
// http://localhost:3333/customer/e430ed07-6c4c-4cb8-ba8b-65523797530a
namespace ResourceHandling.Customer
{
  using System;

  using Simple.Web;
  using Simple.Web.Behaviors;

  [UriTemplate("/customer/{CustomerUid}")]
  public class GetEndpoint : IGet, IOutput<CustomerModel>
  {
    private ICustomerQuery customerQuery;

    public GetEndpoint(ICustomerQuery customerQuery)
    {
      this.queryCustomer = queryCustomer;
    }

    public Guid CustomerUid { get; set; }

    public CustomerModel Customer { get; set; }

    public Status Get()
    {
      // Locate the customer by this.CustomerUid
      this.Customer = this.customerQuery.Execute(this.CustomerUid);

      // Return 200 OK using Simple.Web's status helper
      return Status.OK;
    }
  }
}
// [POST] PostEndpoint.cs
namespace ResourceHandling.Customer
{
  using System;

  using Simple.Web;
  using Simple.Web.Behaviors;

  [UriTemplate("/customer")]
  public class PostEndpoint : IPost, IInput<CustomerModel>
  {
    private ICustomerCommand customerCommand;

    public PostEndpoint(ICustomerCommand customerCommand)
    {
      this.customerCommand = customerCommand;
    }

    public CustomerModel Customer { get; set; }

    public Status Post()
    {
      // Need to create or update this.CustomerUid
      this.customerCommand.Execute(this.Customer);

      // We should return 201 (Created) and we'll lose the status helper ;-)
      return 201;
    }
  }
}

Representation

We haven't talked about how your resource state is represented back to the client (the "Re" in ReST); this is the subject of another page on Content Negotiation.