Search code examples
c#restasp.net-web-api2hateoashypermedia

Proper approach to create hypermedia in c# webapi


I'm doing some research on how to implement hypermedia for a particular resource, but can't find a real implementation example, just abstractions...

You know, in various articles, the guy create a method like:

public List<Link> CreateLinks(int id)
{
    ...//Here the guy put these three dots, whyyyyyyyyyy?
}

What I have so far:

public Appointment Post(Appointment appointment)
    {
        //for sake of simplicity, just returning same appointment
        appointment = new Appointment{
            Date = DateTime.Now,
            Doctor = "Dr. Who",
             Slot = 1234,
            HyperMedia = new List<HyperMedia>
            {
                new HyperMedia{ Href = "/slot/1234", Rel = "delete" },
                new HyperMedia{ Href = "/slot/1234", Rel = "put" },
            }
        };

        return appointment;
    }

And the Appointment class:

public class Appointment
{
    [JsonProperty("doctor")]
    public string Doctor { get; set; }

    [JsonProperty("slot")]
    public int Slot { get; set; }
    [JsonProperty("date")]
    public DateTime Date { get; set; }

    [JsonProperty("links")]
    public List<HyperMedia> HyperMedia { get; set; }
}

public class HyperMedia
{
    [JsonProperty("rel")]
    public string Rel { get; set; }

    [JsonProperty("href")]
    public string Href { get; set; }
}

Is there a proper way to that? I mean, without hard coding the links? How to create them dynamically for a given type, i.e. Appointment class?

I'm using c# Webapi, not c# MVC.


Solution

    1. For adding routes dynamically to the HyperMedia collection you can make use of route naming:

      • Define your route with a specific name (for example for deletion):

        [Route("{id:int}", Name = "AppointmentDeletion")]
        public IHttpActionResult Delete(int slot)
        {
            //your code
        }
        
      • Use it by UrlHelper.Link method:

        public Appointment Post(Appointment appointment)
        {
            appointment = new Appointment
            {
                HyperMedia = new List<HyperMedia>
                {
                    new HyperMedia
                    { 
                        Href = Url.Link("AppointmentDeletion", new { slot = 1234 }), 
                        Rel = "delete" 
                    }
                }
        
            return appointment;
        }; 
        
    2. It is also possible to add links dynamically to a result object without declaring the HyperMedia property for every class:

      • Define a class without links:

        public class Appointment
        {
            [JsonProperty("doctor")]
            public string Doctor { get; set; }
        
            [JsonProperty("slot")]
            public int Slot { get; set; }
        
            [JsonProperty("date")]
            public DateTime Date { get; set; }
        } 
        
      • Define an extension method:

        public static class LinkExtensions
        {
            public static dynamic AddLinks<T>(this T content, params object[] links)
            {
                IDictionary<string, object> result = new ExpandoObject();
        
                typeof (T)
                    .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                    .ToList()
                    .ForEach(_ => result[_.Name.ToLower()] = _.GetValue(content));
        
                result["links"] = links;
        
                return result;
            }
        }
        
      • Use it:

        public IHttpActionResult Post(Appointment appointment)
        {
            return Ok(appointment.AddLinks(new HyperMedia
            { 
                Href = Url.Link("AppointmentDeletion", new { slot = 1234 }), 
                Rel = "delete" 
            }));
        }