Controllers

Live on this server

Uses controllers to implement a web service, similar to ASP.NET Core MVC.

The book shelf API once more, in the style of MVC controllers: every public method of BookController is an action, and its name is its path - Index() answers /books/, Create() answers /books/create/ and Delete() answers /books/delete/1/.

[ControllerAction] sets the HTTP method of an action, and [FromPath] takes a parameter from the path instead of the query. Controllers suit APIs with many actions, where a naming convention saves declaring every route.

To run it, create a console app (dotnet new console), add GenHTTP (dotnet add package GenHTTP.Full), replace Program.cs with the code below and start it with dotnet run - it answers at http://localhost:8080/.

Program.cs58 lines
using GenHTTP.Engine.Internal;

using GenHTTP.Api.Content;
using GenHTTP.Api.Protocol;

using GenHTTP.Modules.Controllers;
using GenHTTP.Modules.Layouting;
using GenHTTP.Modules.Practices;
using GenHTTP.Modules.Reflection;

var app = Layout.Create()
                .AddController<BookController>("books");

await Host.Create()
          .Handler(app)
          .Defaults()
          .RunAsync();

public record Book(int Id, string Title);

public class BookController
{
    private static readonly List<Book> Books = [new(1, "Lord of the Rings")];

    // GET /books/
    [ControllerAction(Method.Get)]
    public List<Book> Index()
    {
        lock (Books) return [..Books];
    }

    // PUT /books/create/ with a book as JSON, XML, ...
    [ControllerAction(Method.Put)]
    public Book Create(Book book)
    {
        lock (Books)
        {
            if (Books.Count >= 50)
            {
                throw new ProviderException(ResponseStatus.Conflict, "The shelf is full, please delete a book first");
            }

            var added = book with { Id = Books.Select(b => b.Id).DefaultIfEmpty().Max() + 1 };

            Books.Add(added);

            return added;
        }
    }

    // DELETE /books/delete/1/
    [ControllerAction(Method.Delete)]
    public void Delete([FromPath] int id)
    {
        lock (Books) Books.RemoveAll(b => b.Id == id);
    }

}

Try it

Send the requests to the sample running on this server - change the path, headers or body as you like, or copy them as curl commands.

  1. List all books

    GET
    curl -i 'https://genhttp.org/demo/controllers/books/'
  2. Add a book

    PUT
    Headers and body
    curl -i -X PUT -H 'Content-Type: application/json' -d '{ "title": "The Hobbit" }' 'https://genhttp.org/demo/controllers/books/create/'
  3. Delete a book

    DELETE
    Headers and body
    curl -i -X DELETE 'https://genhttp.org/demo/controllers/books/delete/1/'