/Carter.SirenNegotiator

A library that extends Carter to handle Siren requests.

Primary LanguageC#MIT LicenseMIT

Carter.SirenNegotiator

Build status NuGet

Carter.SirenNegotiator adds content negotiation for Siren requests to your Carter API.

Installation

Packaage Manager

PM> Install-Package Carter.SirenNegotiator

.Net CLI

$ dotnet add package Carter.SirenNegotiator

Usage

Taken from the sample project.

Create a Response Generator

public class ActorResponseGenerator : ISirenResponseGenerator
{
    public bool CanHandle(Type type)
    {
        var listType = typeof(IEnumerable<Actor>);
        var classType = typeof(Actor);
        return classType.IsAssignableFrom(type) || listType.IsAssignableFrom(type);
    }

    public Siren Generate(object data, Uri uri)
    {
        return data is IEnumerable<Actor> 
            ? Generate((IEnumerable<Actor>) data, uri) 
            : Generate((Actor) data, uri);
    }

    private Siren Generate(IEnumerable<Actor> actors, Uri uri)
    {
        var doc = new Siren
        {
            @class = new [] { "collection" },
            entities = new List<Entity>(),
            properties = new { Count = actors.Count() }
        };

        foreach (var actor in actors)
        {
            var entity = new Entity
            {
                @class = new [] { nameof(Actor) },
                rel = new [] { "item" },
                properties = actor,
                links = new List<Link> { new Link { href = uri + "/" + actor.Id, rel = new [] { "self" } } }
            };

            doc.entities.Add (entity);
        }

        doc.actions = new List<Action> (new []{

            new Action
            {
                name = "create-actor",
                title = "Create Actor",
                method = "POST",
                href = uri.ToString(),
                type = "application/json",
                fields = new List<Field>(new[] {new Field {name = "name", type = "text"}, new Field{name = "age", type = "number"}})
            }
        });

        doc.links = new List<Link> { new Link { href = uri.ToString(), rel = new [] { "self" } } };

        return doc;
    }
    
    private Siren Generate(Actor actor, Uri uri)
    {
        return new Siren
        {
            @class = new [] { nameof(Actor) },
            properties = actor,
            links = new List<Link> { new Link { href = uri.ToString(), rel = new [] { "self" } } },
            actions = new List<Action> (new []{
                new Action
                {
                    name = "update-actor",
                    title = "Update Actor",
                    method = "PUT",
                    href = uri.ToString(),
                    type = "application/json",
                    fields = new List<Field>(new[] {new Field {name = "name", type = "text"}, new Field{name = "age", type = "number"}})
                },
                new Action
                {
                    name = "delete-actor",
                    title = "Delete Actor",
                    method = "DELETE",
                    href = uri.ToString()
                }
            })
        };
    }
}

This respose generator can handle a list of actors IList<Actor> or a single Actor.

Register Your Response Generator

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IActorProvider, ActorProvider>();
    services.AddScoped<ISirenResponseGenerator, ActorResponseGenerator>();
    
    services.AddCarter();
}

Create Your Module

public class ActorsModule : CarterModule
{
    public ActorsModule(IActorProvider actorProvider)
    {
        Get("/actors/{id:int}", async (req, res, routeData) =>
        {
            try
            {
                var person = actorProvider.Get(routeData.As<int>("id"));
                await res.Negotiate(person);
            }
            catch (InvalidOperationException)
            {
                res.StatusCode = 404;
            }
        });
    }
}

Send a Request

$ curl -H "Accept: application/vnd.siren+json" http://localhost:5000/actors/1
{
  "class": [
    "Actor"
  ],
  "properties": {
    "Name": "Brad Pitt",
    "Id": 1,
    "Age": 51
  },
  "actions": [
    {
      "name": "update-actor",
      "title": "Update Actor",
      "method": "PUT",
      "href": "http://localhost:5000/actors/1",
      "type": "application/json",
      "fields": [
        {
          "name": "name",
          "type": "text"
        },
        {
          "name": "age",
          "type": "number"
        }
      ]
    },
    {
      "name": "delete-actor",
      "title": "Delete Actor",
      "method": "DELETE",
      "href": "http://localhost:5000/actors/1"
    }
  ],
  "links": [
    {
      "rel": [
        "self"
      ],
      "href": "http://localhost:5000/actors/1"
    }
  ]
}

Contributing

Carter.SirenNegotiator is a community project. We invite your participation through issues and pull requests!

License

Carter.SirenNegotiator is licensed under MIT. Refer to LICENSE for more information.