GithubHelp home page GithubHelp logo

webapi.hal's Introduction

WebApi.Hal

Adds support for the Hal Media Type (and Hypermedia) to Asp.net Web Api

HAL Specification

http://stateless.co/hal_specification.html
https://github.com/mikekelly/hal_specification

Getting Started with WebApi.Hal

First thing first, WebApi.Hal is a media formatter. So to get started you have to register the Hal Media formatters:

GlobalConfiguration.Configuration.Formatters.Add(new JsonHalMediaTypeFormatter());
GlobalConfiguration.Configuration.Formatters.Add(new XmlHalMediaTypeFormatter());

Once those are registered, you can start defining your resources. In WebAPI.Hal, you always return Representations from your ApiControllers.

Your representations should all inherit from Representation or if you are returning a collection a SimpleListRepresentation<TRepresentation> or if you are returning a resource with multiple embedded resources, inherit from Representation and add properties for the embedded resources which might include lists of resources of the form List<MyRepresentation>. See the sample.

WebApi.Hal.Link

The link class represents hypermedia on a representation. It looks like this:

public class Link
{
	public string Rel { get; set; }
	public string Href { get; set; }
	public string Title { get; set; }
	public bool IsTemplated { get; set; }
	public Link CreateLink(...);
}

The thing which makes this class special is it's support for templates. For example:

var link = new Link("beers", "/breweries/{id}/beers");

Notice the {id}, this allows you to return a templated link as hypermedia. But you can also turn it into an absolute link really easily:

Brewery brewery;
link.CreateLink(new { id = brewery.Id });

URI Templates adhere to RFC6570, as per the HAL specification.

Register WebAPI routes

Once you have done the work of defining all your link templates, register your routes in the ordinary MVC WebApi manner. See the sample project for an example.

LinkTemplates class

A nice place to keep your Links is in a static class called LinkTemplates, with nested classes for each resource your application has, for example:

public static class LinkTemplates {
	public static class Beers {
		/// <summary>
		/// /beers/{id}
		public static Link Beer { get { return new Link("beer", "/beers/{id}"); } }

		/// <summary>
        /// /beers?page={page}
        /// </summary>
        public static Link GetBeers { get { return new Link("beers", "/beers{?page}"); } }
	}
}

The sample is available at: https://github.com/JakeGinnivan/WebApi.Hal/blob/master/WebApi.Hal.Web/LinkTemplates.cs

WebApi.Hal.Representation

This is the base class for all representations your api returns. It has an abstract method you must override, abstract void CreateHypermedia();

In the constructor, set the Rel property, as this generally doesn't change based on context.

In CreateHypermedia() you could register a self link, but it's done automatically for you if you don't. Register other hypermedia that your resource should always have. Other context sensitive hypermedia should be added in the API controller.

Here is an example of the Beer CreateHypermedia override (from the example project, the BeerResource):

public Beer()
{
	Rel = LinkTemplates.Beers.Beer.Rel;
}
protected override void CreateHypermedia()
{
	Href = LinkTemplates.Beers.Beer.CreateLink(new { id = Id }).Href;

	if (StyleId != null)
		Links.Add(LinkTemplates.BeerStyles.Style.CreateLink(new { id = StyleId }));
	if (BreweryId != null)
		Links.Add(LinkTemplates.Breweries.Brewery.CreateLink(new { id = BreweryId }));
}

Sample controller action

public BreweryRepresentation Get(int id)
{
	var brewery = beerDbContext.Breweries.Find(id);

	return new BreweryRepresentation
	{
		Id = brewery.Id,
		Name = brewery.Name,
		Links =
		{
			LinkTemplates.Breweries.AssociatedBeers.CreateLink(new { id })
		}
	};
}

Sample Project

To run the sample project, update the connection string in web.config, then create the database. When you hit an API for the first time, the database will be setup using DbUp.

You can use fiddler to explore the API. Make sure you put in an accept header of application/hal+json. Try hitting http://localhost:51665/beers with that accept header, and see what happens

Credits

I have more credits to add, but this is the most obvious (as I based my Xml formatter off this project)

https://bitbucket.org/smichelotti/hal-media-type

Release Notes

See Readme.txt at http://github.com/JakeGinnivan/WebApi.Hal

webapi.hal's People

Contributors

askingalot avatar bryant1410 avatar cosmin-ciuc avatar danbarua avatar darrent avatar gabrielweyer avatar jakeginnivan avatar jetski5822 avatar johnnysaucepn avatar jtone123 avatar kbrichan avatar kirsar avatar kuehlmeyer avatar marner2 avatar mladenb avatar panmanphil avatar programatt avatar roberthahn-ais avatar shiftbot avatar tboyce avatar vsliouniaev avatar wis3guy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

webapi.hal's Issues

Double linking

Hello,

I am confused by need for the <links> section, especially given a collection. For example, I would think the following:

<item xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ItemService">
  <Href xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal">~/api/items/14</Href>
  <LinkName xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal" i:nil="true"/>
  <Links xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal">
    <Link>
      <Href>~/api/items/14</Href>
      <Rel>self</Rel>
      <Title i:nil="true"/>
    </Link>
    <Link>
      <Href>~/api/items/14/widgets</Href>
      <Rel>widgets</Rel>
      <Title i:nil="true"/>
    </Link>
    <Link>
      <Href>~/api/items/14/widgets</Href>
      <Rel>widgets</Rel>
      <Title i:nil="true"/>
    </Link>
    <Link>
      <Href>~/api/items/14/widgets</Href>
      <Rel>widgets</Rel>
      <Title i:nil="true"/>
    </Link>
    <Link>
      <Href>~/api/items/14/widgets</Href>
      <Rel>widgets</Rel>
      <Title i:nil="true"/>
    </Link>
  </Links>
  <Rel xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal">self</Rel>
  <id>14</id>
  <name>itemName</name>
  <widgets xmlns:d2p1="http://schemas.datacontract.org/2004/07/ItemService.Resources.List">
    <d2p1:widget>
      <Href xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal">~/api/items/14/widgets/1</Href>
      <LinkName xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal" i:nil="true"/>
      <Links xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal">
    <Link>
      <Href>~/api/items/14/widgets/1</Href>
      <Rel>self</Rel>
      <Title i:nil="true"/>
    </Link>
      </Links>
      <Rel xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal">widgets</Rel>
      <d2p1:itemId>14</d2p1:itemId>
      <d2p1:widgetId>1</d2p1:widgetId>
    </d2p1:widget>
    <d2p1:widget>
      <Href xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal">~/api/items/14/widgets/2</Href>
      <LinkName xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal" i:nil="true"/>
      <Links xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal">
    <Link>
      <Href>~/api/items/14/widgets/2</Href>
      <Rel>self</Rel>
      <Title i:nil="true"/>
    </Link>
      </Links>
      <Rel xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal">widgets</Rel>
      <d2p1:itemId>14</d2p1:itemId>
      <d2p1:widgetId>2</d2p1:widgetId>
    </d2p1:widget>
    <d2p1:widget>
      <Href xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal">~/api/items/14/widgets/3</Href>
      <LinkName xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal" i:nil="true"/>
      <Links xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal">
    <Link>
      <Href>~/api/items/14/widgets/3</Href>
      <Rel>self</Rel>
      <Title i:nil="true"/>
    </Link>
      </Links>
      <Rel xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal">widgets</Rel>
      <d2p1:itemId>14</d2p1:itemId>
      <d2p1:widgetId>3</d2p1:widgetId>
    </d2p1:widget>
    <d2p1:widget>
      <Href xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal">~/api/items/14/widgets/4</Href>
      <LinkName xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal" i:nil="true"/>
      <Links xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal">
    <Link>
      <Href>~/api/items/14/widgets/4</Href>
      <Rel>self</Rel>
      <Title i:nil="true"/>
    </Link>
      </Links>
      <Rel xmlns="http://schemas.datacontract.org/2004/07/WebApi.Hal">widgets</Rel>
      <d2p1:itemId>14</d2p1:itemId>
      <d2p1:widgetId>4</d2p1:widgetId>
    </d2p1:widget>
  </widgets>
</item>

would have been created as:

<item xmlns="http://schemas.datacontract.org/2004/07/ItemService" 
      xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
      hal="http://schemas.datacontract.org/2004/07/WebApi.Hal">
  <hal:Href>~/api/items/14</Href>
  <hal:Rel>self</Rel>
  <hal:LinkName i:nil="true"/>
  <id>14</id>
  <name>itemName</name>
  <widgets>
    <widget>
      <hal:Href>~/api/items/14/widgets/1</Href>
      <hal:LinkName i:nil="true"/>
      <hal:Rel>self</Rel>
      <itemId>14</itemId>
      <widgetId>1</widgetId>
    </widget>
    <widget>
      <hal:Href>~/api/items/14/widgets/2</Href>
      <hal:LinkName i:nil="true"/>
      <hal:Rel>self</Rel>
      <itemId>14</itemId>
      <widgetId>2</widgetId>
    </widget>
    <widget>
      <hal:Href>~/api/items/14/widgets/3</Href>
      <hal:LinkName i:nil="true"/>
      <hal:Rel>self</Rel>
      <itemId>14</itemId>
      <widgetId>3</widgetId>
    </widget>
    <widget>
      <hal:Href>~/api/items/14/widgets/4</Href>
      <hal:LinkName i:nil="true"/>
      <hal:Rel>self</Rel>
      <itemId>14</itemId>
      <widgetId>4</widgetId>
    </widget>
  </widgets>
</item>

The second listing is much more compact and, unless I am missing something, contains all the same relevant information.

Is it possible to remove the <links> section?

What is the purpose of the four repetitive /item/Links/Link nodes in the first listing?

Am I creating the collection incorrectly?

Thanks in advance.

CreateHypermedia method call

Calling the CreateHypermedia method as part of the protected constructor in Representation does not make much sense when the object has not been completely initialized yet. If you use the object initializer syntax for creating the instance of the Representation class, the CreateHypermedia will be called before the object is in a valid state. Also, I think it would be helpful to have a CreateHypermedia override that receives the UrlHelper class provided in the framework to resolve the links from the configured routes.

I can probably try a few alternatives and send a pull request if that works for you

Thanks

Should _links be a collection?

Hi,

Have implemented your changes into my project and everything works great, SimpleListRepresentation makes things a lot easier.

Was wondering looking at the JSON return if _links should be returned as a collection, i.e. _links : [ rather than as a series of objects _links : { ??

This change would make the processing of the JSON returned from an AJAX call to the client much easier to process with a library like Knockout.JS for example.

Your thoughts are very welcome and many thanks for sharing this fantastic project.

Shaun

Unable to call breweries resource in sample project

I just cloned the repo, built and ran the sample web app, and tried browsing /api/breweries and got the following NotSupportedException exception:

"In constructors and initializers, only property or field parameter bindings are supported in LINQ to Entities."

HAL spec compliance enhancement 1 of 3 - links

The HAL spec says of links: It is an object whose property names are link relation types (as defined by RFC5988) and values are either a Link Object or an array of Link Objects.
WebApi.Hal supports "a Link Object" but not "an array of Link Objects".
Consider this sample from a Mike Kelly blog:

"_links": {
  "self": {"href": "/product/987"},
  "upsell": [
    {"href": "/product/452", "title": "Flower pot"},
    {"href": "/product/832", "title": "Hover donkey"}
  ]
}

WebApi.Hal supports the "self" example as "a Link Object", but not "upsell" as "an array of Link Objects".
Adding the second "upsell" link via HypermediaList.Add overwrites the first one.
Adding the second "upsell" link via HypermediaList.Insert adds it, but then Representation throws a duplicate key exception when creating a dictionary of links.

I propose to:

  • modify LinksConverter to output the single Link Objects as is, but to support "an array of Link Objects" as in the "upsell" sample, possibly through creating a temporary Lookup collection
  • in order to allow multiple links with the same rel, modify HypermediaList.Add to always add instead of overwriting
  • since dictionary only allows one entry per key, must modify Representation to not create a dictionary keyed by rel, but simply use a JsonPropery on Links to serialize name as "_links"
  • modify LinksConverter to output the templated property as "templated" according to the standard, instead of "isTemplated"
  • modify the beer sample to have something demonstrating "an array of Link Objects", similar to the "upsell" above
  • the current CreateHypermedia pattern does three things: sets Href and Rel properties, creates "self", and creates other links. This happens sort of as a side effect during serialization by referencing Rel or Href or enumerating the links. This side effect nature contributes to the re-entrant problems noted in the source code comments. Generating hypermedia can be elevated to a first class citizen, firing at a known, deterministic time, and can therefore vastly simply the code and eliminate the re-entrant problems.
  • after it's all said and done, HypermediaList is no longer needed because it is reduced to nothing more than a decorator of a list with no more value add, so it is eliminated.
  • add unit tests for xml and json to demonstrate the new array of Link Objects

This is a non-breaking change, will comply with the standard, and will fix several exceptions when using hal+json..

XML Formatter only serialises simple types

Hi,

Have a class object which contains sub-objects. Serialises correctly to JSON, the XML Formatter ignores the sub-objects, traced this to the following code fragment:

        public static bool IsValidBasicType(this PropertyInfo property)
        {
            return !NonSerializedProperties.Contains(property.Name) && property.PropertyType.Namespace == "System"
                   && (property.PropertyType.IsValueType || property.PropertyType == typeof(string));
        }

which gets called by WriteResourceProperties, wondered if there was a specific reason for doing this?

Many thanks,

Shaun

HAL spec compliance enhancement 3 of 3 - uri templates

The spec says of "href": Its value is either a URI [RFC3986] or a URI Template [RFC6570].
WebApi.Hal support RFC3986 URI, but not RFC6570 URI Template. The templates currently specified in the sample are not RFC6570. And, the parsing of URI Templates in Link.RegisterLinkWithWebApi does not understand RFC6570, but expects something else.
For example, this link in LinkTemplates.cs "/beers?searchTerm={searchTerm}&page={page}", is not RFC6570. Expressed in RFC6570 terms, it would be "/beers{?searchTerm,page}".
However, this link in LinkTemplates.cs "/beers?page={page=1}" is problematic. Expressed in RFC6570, it would be "/beers{?page}", but we lose the default used for route generation.
As far as I can tell, the current WebApi.Hal URI templates serve 3 purposes: a) for generating MVC routes, b) for resolving, via substitution, the non-templated "self" link and other non-templated links, and c) for outputting templated links. Generating MVC routes from the templates is in conflict with the other two purposes, since the route code expects a certain syntax and defaults are not compliant with the standard.

I propose to:

  • use the level-4 compliant RFC6570 parser at https://github.com/tavis-software/UriTemplates
  • resolve non-templated links using the RFC6570 compliant parser
  • output templated links as RFC6570 compliant hrefs instead of the current non-standard
  • do not use templates to generate MVC routes, since a) routes are easily specified using standard MVC machinery, b) the sample doesn't work anyway with the routes generated by templates (problems with BeersController.Search and others), c) too much of an overloaded purpose for templates.
  • fix a bunch of case-sensitive problems in the current sample regarding link substitution - RFC6570 states params are case sensitive

This is a breaking change for anyone relying on routes being generated by WebApi.Hal, but I'm not sure anyone could be fully relying on it since it doesn't seem to thoroughly work in the first place.

SimpleListRepresentation - Rel issue

Hello everyone!

I try to implement a generic class to use the SimpleListRepresentation but I have a little issue with the Rel attribute.
My class looks like this:

public class HalRepresentationList : SimpleListRepresentation where TResource : IResource
{
public HalRepresentationList(IList tasks)
: base(tasks)
{
}

    protected override void CreateHypermedia()
    {
        this.Href = "testHref";
        this.Rel = "tasks";
        this.LinkName = "testLinkName";
    }
}

When I test its serialization, the _embedded resource does not use correctly (it does not show it) the Rel attribute like in the image below:
tasks_bad

It should look like this:

tasks_good

I have to mention that this issue is on the current version on NuGet. I've got the code from github, and it shows "unknownRel-TaskHalRepresentation".
I looked in the code for this, and found out that in the _embedded resource, the Rel attribuite is taken from each resource in the list of resources as the next line speaks:

Embedded = resourceList.Count > 0 ? resourceList.ToLookup(r => r.Rel) : null;

I've changed this line with:

Embedded = resourceList.Count > 0 ? resourceList.ToLookup(r => this.Rel) : null;

and it works.
Is is correct, or I should set the Rel attribuite on each resource in the list of resources in order to get the Rel attribute of the entire list as it should be?

Thank you!
Mihai

HAL spec compliance enhancement 2 of 3 - embedded

The spec says of embedded: It is an object whose property names are link relation types (as defined by [RFC5988]) and values are either a Resource Object or an array of Resource Objects.
WebApi.Hal supports a single "array of Resource Objects" but not "a Resource Object" nor a mix of multiple relation types.
Consider this sample from a Mike Kelly blog, abbreviated for clarity:

"_embedded": {
  "manufacturer": {"name": "Manufacturer Inc."},
  "review": [
    {"title": "Love it!",
      "content": "I love this product. I also bought a Hover Donkey with it.",
      "rating": 10},
    {"title": "Hate it!",
      "content": "My hover donkey choked on it. DO NOT BUY!!!",
      "rating": 0}
  ]
}

WebApi.Hal supports "review" as "an array of Resource Objects", as long as "review" is the only property of "_embedded". But it does not support "manufacturer" as "a Resource Object", nor does it support the inclusion of both "manufacturer" and "review".

I propose to:

  • modify the serialization of Resource (JsonConverters/ResourceConverter.cs) to look for contained properties of type IResource or lists of IResource and serialize them as _embedded as per the sample above

  • this allows defining representation classes such as this, where you want a resource to contain heterogenous sets of embedded resources:

    public class BeerDetailRepresentation : Representation
    {
        public BeerDetailRepresentation()
        {
            Reviews = new List<ReviewRepresentation>();
        }
        public int Id { get; set; }
        public string Name { get; set; }
        public BeerStyleRepresentation Style { get; set; }
        public BreweryRepresentation Brewery { get; set; }
        public List<ReviewRepresentation> Reviews { get; protected set; }
    }
    
  • there is no need to have an additional Rel property of a list itself, since the Rel of the array is derived from the Rel of the individual items in the list (this is different from the current RepresentationList which assumes a List has a separate Rel from the contents of the list, which is a departure from the standard)

  • modify the beers sample to have something demonstrating "an array of Resource Objects", similar to the "review" above

  • modify the beers sample to have something demonstrating a mix of multiple embedded resources, similar to the style, brewery AND reviews above

  • retain RepresentationList for now, but mark it as deprecated

  • implement a replacement for RepresentationList as a convenience for containing one simple homogeneous list, but implement it as a Resource containing a list, rather than BEING a list as the current RepresentationList is done

Doing it the way proposed is not a breaking change today.

Resources without self link (or _links)!

I want to be able to serialize a resource without the self link (also without the _links). When I don't set the Href attribute, in RepopulateHyperMedia() we have:

if (Links.Count(l=>l.Rel == "self") == 0)
Links.Insert(0, new Link { Rel = "self", Href = Href });

but the Href is null. Then I get an error - "Value cannot be null. Parameter name: virtualPath" - in public string ResolveUri(string href) from LinksConverter.cs.

I would test also if the Href is not null as follows:

if (Links.Count(l=>l.Rel == "self") == 0 && Href != null)
Links.Insert(0, new Link { Rel = "self", Href = Href });

Acording to hal-json specification:

4.1.1. _links
The reserved "_links" property is OPTIONAL.

Does it makes sense to you also?
Thank you!

Does WebApi.Hal support object of links instead of IList<Link>?

I have used Taverson and it looks like it only looks for object[key] in _links and not loop through the array of links

For example, my json would look like this after it's generated.

"_links" : [ { ... Link Object }, { ... Another Link Object } ]

Which Traverson cannot support this representation. Any suggestions is very appreciated.

Suggestion for improvement of the Link and UriTemplate classes

Currently, when you specify a link as a template like this:

~/users/{userid}/friends/{friendid}/interactions

... and then, in the UserRepresentation, where i know the value for the {userid} but not for the {friendid}, i want to return the above link, i need to call:

Links.Add(LinkTemplates.Users.InteractionList.CreateLink(new {userid = 1}));

As a result my url will become: /users/1/friends//interactions

What i would like in this case, is that the Link class, and the backing UriTemplate class would return a new Link instance, which is still a template, having the following href: ~/users/1/friends/{friendid}/interactions

In my use-case, the value for {friendid}, is known only to the consumer. I do not have these values in my backing data store. I actually would like to return a templated link to my clients.

For now, i work around the issue by defining this link like this:

~/users/{userid}/interactions{?friendid}

In conclusion, I suggest that the CreateLink, CreateUri and SubstituteParams methods get overloads that will result in partially resolved uri's and thus links that will be serialized in the response with the "templated" (http://tools.ietf.org/html/draft-kelly-json-hal-06#section-5.1) value set accordingly.

virtual Rel and Href

We've discovered where Href would be very useful to know prior to CreateHypermedia, specifically in the Filter phase of the WebApi HTTP pipeline.

Currently, three hypermedia things happen at different times: 1) the object's Rel is set in c-tor, 2) the object's Href and links are set in CreateHypermedia, and 3) during serialization (after CreateHypermedia) "self" is created if not already created.

In our case, Href can be formed whenever the object's id properties are set, and so doesn't really need to defer to CreateHypermedia. And, in our case, Rel is fixed, meaning it could be a const string returned by the Rel getter.

Therefore, we've found ourselves wishing Representation's Href and Rel had no setter, the getter could be referenced at any time without needing CreateHypermedia to be called, and the getter was defined as abstract which forces the derived class to think about them.

This way, each Representation-derived object could implement Rel like this (in practice, we'd use a link template as in the beer sample):

public override string Rel { get { return "myrel"; } }

Href could be similar, callable at any time, and would generate the Href based on a link template + content.

Changing the Representation this way is a breaking change, so a compromise is to merely change Representation Href and Rel to be virtual. This way, the override would be:

public override string Rel { get { return "myrel"; } set { } }

Existing code setting the Rel in the c-tor would get a warning from ReSharper, but otherwise it should work. What do you think?

Response content-type header not "application/hal+json"

In my project i return Representation instances from my action methods and i have configured the formatters as described in the readme (actually i copied the code from the demo project). My responses seem to be properly formatted, so i feel there is no mistake on my side there.

What bothers me is that the "content-type" header in my responses is simply "application/json" and not "application/hal+json" as it should be, according to the spec (http://tools.ietf.org/html/draft-kelly-json-hal-06#section-3).

I've downloaded the source and looked through it, but apparently the library does not enforce the correct content type on the response. I've also done some research and the way i understand it, you cannot manipulate the response headers from within a formatter.

How is this normally resolved?

approx half tests fail when cloning and running in VS 2013 integrated runner

I was able to make them work by removing all instances , s => s.Replace("\r\n", "\n")

Surely this either reflects a shortcoming in the test impl of in the repo's .gitsettings ?

The exceptions are peopledetail_get_json_test and one_item_organisation_list_get_json_test, which fails regardless

Packages.config seems to have json.net 6.0.2 and I didnt change it?

a solution to remind people to use accept header of application/hal+json

People regularly report bugs, thinking this library isn't working, only to find out they forgot the accept header. How about if we help them a bit in the ordinary JSON response? We could put a const string in the Representation class which only gets serialized to JSON, not hal+json. It could read something like:
"hey": "you're using the webapi.hal library but you're not using the accept header to get hal+json; so this object is ordinary JSON, not hal+json"

Potential Bug

I have noticed this code:
[JsonIgnore]
public string Rel
{
get
{
// Prevent CreateHypermedia from being reentrant to this method
if (creatingHyperMedia || selfLinkUpToDate)
return href;
creatingHyperMedia = true;
try
{
CreateHypermedia();
}
finally
{
creatingHyperMedia = false;
}
return rel;
}
set
{
rel = value;
selfLinkUpToDate = false;
}
}
Should the first "return href;" actually be "return rel;"

Define absolute URI ony once

Is there a way to define absolute URI only once in some config parameter rather then using String.Format() or some similar method when defining links as show

var templateLink = new Link("beerbyname", "http://myserver.com/api/beers/{name}");
// act
var link = templateLink.CreateUri(new {name = "BeerName"});
// assert
Assert.Equal("http://myserver.com/api/beers/BeerName", link.ToString());
?

Deserialization does not work.

While JsonHalFormatter can serialize resources adequately it doesn't do much about deserializing them. I've extended the unit tests for serialization to attempt to deserialize the serialized result it it returns null every time.

Serialization of one-item SimpleListRepresentation

Hi there,

I'd like to ask, why is desired to serialize one item SimpleListRepresentation collection as object and not as array? Programmer knows, that resulting data are array type and the count can not change format from array to object.

curie, profile and type support

There are some pretty key link attributes missing from Link. Of these curies are the most important. If you state a rel, a name that describes what happens, why you are doing it, when you follow a link, it must be one of:

  • IANA registered link relation, can be listed as plain text
  • a full URI, in essence a URL that points to a specific document
  • a "namespaced" aka curie link of the form : where the curie name will refer to a link to a document to be used as a link relation extension. Look at the hypertalk sample application for a simple working example.

profile refers to an IRI, again essentially a URL pointing to a profile document that explains what the data at the end of the link is. iCal would be an example of a profile.

the type attribute allows the link to be a different media type. Since the operating premise for doing forms with HAL, still the subject of intense debate, is to use HTML, having a link that says type="text/html" allows your application/hal+json to say follow this link and get html.

i am working on a pull request, but may need help with round tripping the curies

License

Unless I missed it somewhere, there does not appear to be any kind of license associated with the source.

Generic representations?

I'm trying to figure out a method of returning my models that doesn't involved entirely recreating them into a Representation. The simplest thing I can think of is to make Representation into a generic type. This way I could just do:

public abstract class Representation<TResource>
{
    public Representation(TResource resource)
    {
        this.Resource = resource;
    }
}

public class PersonRepresentation : Representation<Person>
{
}

The vast majority of PersonRepresentation would then use the resource for the fields. In this way I could add additional properties as needed. Obviously I'd need a method of having the representation ignore certain properties as well.

I think this could be done with code similar to AutoMapper's such as:

public class PersonRepresentation : Representation<Person>
{
    public PersonRepresentation(Person person)
    {
         this.Resource = person;
         this.CustomizeResource(person => person.Password, options => options.HideFromRepresentation());
    }
}

Has there been any thought on pursuing this sort of strategy?

Any plans to support ASP.NET Web Api 2.2?

The dependency constraint of >= 5.1.2 && < 5.2 on these NuGet packages prevents use of WebApi.Hal from being added to projects setup with Web Api 2.2 package already added.

Thanks

Links not serialized as a JSON collection

The JSON output of the Links collection is not represented as a collection. Instead, it looks something like:
"_links": {
"self": {
"href": "api/myresource"
},
"related1": {
"href": "api/myfirstrelatedresource"
},
"related2": {
"href": "api/mysecondrelatedresource"
}
}

Cannot use string variables when creating a link

I have several links on a resource that share most of a URI, so i tried to place the URL in a variable to format (even if this format happens before the variable is used) breaks the formatter.

so:

string surveyItemUrlFormat = "/surveys/{SurveyId}/items/{Id}/{0}/";
string notesUrl = string.Format(surveyItemUrlFormat, "notes");

Links.Add(new Link("Notes", notesUrl).CreateLink(new { SurveyId, Id }));

Breaks, and i have to go with hardcoded strings:

Links.Add(new Link("Notes", "/surveys/{surveyId}/items/{id}/notes").CreateLink(new { SurveyId, Id }));

Attribute to override rel as convenience for serializing and for disambiguating deserialization

Let's enhance this project to allow attributing embedded properties with a rel override, such as this:

public class AuthorRepresentation : Representation {
   [Rel("recentPosts")]
   public List<PostResource> RctPosts { get; set; }
   [Rel("popularPosts")]
   public List<PostResource> PopPosts { get; set; }
}

The net result is to output json like this:

{
    "_embedded": {
        "recentPosts": [
            {"someProp1": "someValue1"},
            {"someProp1": "someValue2"}
        ],
        "popularPosts": [
            {"someProp1": "someValue3"},
            {"someProp1": "someValue4"}
        ]
    }
}

The common thing currently is to just have PostResource hardcode its rel to something. So to accomplish the above json with the current implementation without the Rel override attribute, one would have to set the rel of each of the objects in the RctPost list to one rel and the objects in PopPosts to another.

Also, upon deserializing, without the Rel override attribute, the deserializer would have no idea which list to put the incoming PostResource objects into.

lowerCamelCase for Resource Properties

Is there a way to enable lowerCamelCase for Resource Properties?

The only way I found to enable it, was modifying the ResourceConverter here and add this line of code:

serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();

This would ease the usage when dealing with JavaScript-Clients.
The specification also uses lowerCamelCase as well as the HAL Browser does.

Another option would be to introduce a property SerializerSettings for the ResourceConverter which would allow the user of WebApi.Hal to decide itself which casing he prefers.

The JsonHalMediaTypeFormatter then also must be aware of this property and maybe could expose a property ResourceConverterSerializerSettings.

HAL Specification and Links

I'm new to HAL, so forgive me if I'm wrong, but in your sample app, it looks like links is always an array. But in the HAL documentation, it looks like links is an object.

For example, your links for a beer, looks like:

_links: [
{
Rel: "self"
Href: "/beers/14"
IsTemplated: false
}
{
Rel: "style"
Href: "
/styles/2"
IsTemplated: false
}
{
Rel: "brewery"
Href: "~/breweries/2"
IsTemplated: false
}
]

But a typical HAL links object would look more like:

_links": {
"self": { "href": "/beers/14" },
"style": { "href": "
/styles/2", "templated":false },
"brewery": { "href": "~/breweries/2", "templated":false }
}

Is there a way to make a Representation serialize links like an object rather than an array?

It seems like you may want to make the Links property in Representation into an object so we can add self, next, previous, style, brewery, or any other "link" properties to it so it serializes like the HAL spec rather than always as an array. Maybe make a generic Representation object that would set the Links property to type BeerLinks and allow us to create a BeerLinks object with some default and custom Link properties.

Question about list representation

Is this wanted scenario that embedded resources in the SimpleListRepresentation get serialized as object if there is only one and as a array if there more than one?

I used following code to generate responses

public class FooRepresentation: Representation {
  public static Link Link { get { return new Link("foo", "~/foos/{id}"); } }

  public string Id { get; set; }
  public string Name { get; set; }

  public override string Rel {
    get { return Link.Rel; }
  }

  public override string Href { 
    get { return Link.CreateLink(new { this.Id }).Href; } 
  }

  protected override void CreateHypermedia() { }
}

public class FooListRepresentation: SimpleListRepresentation<FooRepresentation> {
  public static Link Link { get { return new Link("foos", "~/foos/"); } }

  public FooListRepresentation(IList<FooRepresentation> list): base(list) { }

  protected override void CreateHypermedia() {
    Links.Add(new Link { Href = Link.Href, Rel = "self" });
  }
}

public class FoosController: ApiController {
  public HttpResponseMessage Get() {
    var list = new List<FooRepresentation>();

    list.Add(new FooRepresentation { Id = "14545", Name = "foo" });
    list.Add(new FooRepresentation { Id = "14546", Name = "bar" });

    var resource = new FooListRepresentation(list);

    return this.Request.CreateResponse(HttpStatusCode.OK, resource);
  }
}

so if it returns only one FooRepresentation it serializes it as a object

{
  _links: {
    self: {
      href: "/foos/"
    }
  },
  _embedded: {
    foo: {
      id: "14545",
      name: "foo",
      _links: {
        self: {
          href: "/foos/14545"
        }
      }
    }
  }
}

and if it returns multiple it is as a array

{
  _links: {
    self: {
      href: "/foos/"
    }
  },
  _embedded: {
    foo: [{
      id: "14545",
      name: "foo",
      _links: {
        self: {
          href: "/foos/14545"
        }
      }
    }, {
      id: "14546",
      name: "bar",
      _links: {
        self: {
          href: "/foos/14546"
        }
      }
    }]
  }
}

Can it be configured to use array every time? Because in both cases it is a list just with different amount of items, or did I missed that part in the HAL spec?

Cheers
aruss

how to use "_embedded" property

I have worked with the samples given in the site https://github.com/JakeGinnivan/WebApi.Hal/
In that example I couldn’t find the _embedded resource type sample controller. Is it possible to share which controller/uri has the _embedded resource type.
We are trying to integrate the HAL in our Web API, but I couldn’t find the answers for the following cases,
• How the embedded resources are formed?
• If I want to test the “_embedded” resource, how should I form the entities if I want to represent that?
• Whether entities should be in parent-child relationship to test the _embedded?

PUT deserialization broken?

I'm having trouble with PUT actions. My controller methods are being called but the json representations i'm passing in are showing up as null. Is this a known issue or is there something else I should be looking at on my end?

Hal Browser Support

It would be nice if the output from WebApi.Hal matched the specifications close enough for the HAL Browser to work.

https://github.com/mikekelly/hal-browser

Am I just missing something for this to not work today perhaps? Seems as though the browser doesn't recognize the links as valid.

pull request #59 broke a unit test for single vs. array embedded object

Pull request #59 fixed emitting an embedded array as an array regardless if the array contains just a single element. However, the fix caused ALL embedded objects to get emitted as an array, even for the case where an embedded object property is declared as a single instance. There was an existing unit test for this situation, which now fails.

I discovered this when running the unit tests for the unrelated work I'm now doing on fixing some link problems. I'll fix this array problem now, too, while I'm at it.

Route named 'beers' is already in the route collection?

Hi,

Running the latest version of the project as at 9th Aug, gives the following message...

A route named 'beers' is already in the route collection. Route names must be unique.

This occurs when it attempts to register the route:

LinkTemplates.Breweries.AssociatedBeers.RegisterLinkWithWebApi(routes);

Driving me a bit crazy trying to resolve.

Any help appreciated.

Many thanks,

Shaun

Dynamic object properties missing!

Hello everyone,

I want to be able to add dynamic properties to an object. For example, using a IDictionary<string, string> or even IDictionary<string, object> to be represented as properties of the main object, not as embedded resourses or anything else.
I saw in the code that GetProperties() is used to iterate through the properties of the object to be represented. I've tried to use DLR to dynamically add new properties, but it's quite difficult to achieve it, and must be implemented for every object that we want to have this behaviour.
Maybe using data annotations like:

[RenderAsProperty]
IDictionary<string, string> dynamicProperties;

or

[RenderAsProperty]
IDictionary<string, object> dynamicObjects;

Thank you for your support!
Mihai

Embedded resource question

Why doesn't the property name of a resource or list of resources set the embedded resource's name when serializing? It seems limiting to assume resource's relation is the same for all embedded resources.

Link class has no "Title" property

It looks like the HAL spec allows links to have a title property. This would be great for consumers that want to display that title in the UI as text for hyperlinks, for example.

Hypertext Cache Pattern

Not as much an issue as it is a discussion starter ...

I cannot wrap my head around this section of the HAL spec (https://tools.ietf.org/html/draft-kelly-json-hal-06#section-8.3).

Servers SHOULD NOT entirely "swap out" a link for an embedded resource (or vice versa) because client support for this technique is OPTIONAL.

So does this imply that all embedded resources should also be represented as links in the parent resource? I guess it depends on your starting point; Do you first decide to link something, and then decide it should be embedded, or, do you first decide to embed something and then figure you might as well add the links?

My current opinion is, add a link to each of the embedded resources to the parent resource, thus requiring a code change. Technically, i think it is relatively simple to automatically also generate links for embedded resources to the paren object.

Any thoughts?

Various exceptions and non-conformance issues

Jake, I sent you a separate email regarding a number of exceptions and non-conformance issues. I'll write up separate issues depending on the result of our email discussion. Just wanted to note the email here as a place-holder in case you're not watching your email or my email ends up in your spam folder.

New Nuget Package?

The code in the repo isn't consistent with the Nuget Package. Is this by design?

Deserialize SimpleCollectionRepresentation fails (HAL+XML format only)

Hi,
I'm using WebApi.Hal in both server and client sides of my API.
On the client side, when deserializing SimpleListRepresentation<> in 'HAL+XML', ResourceList collection remains empty. This works fine with HAL+JSON.
I drilled down into the source code to try to spot in the problem and discovered that the issue is located in XmlHalMEdiaTypeFormatter.cs / object ReadHalResource(Type type, XElement xml) method when dealing with collections.

First issue :
the test
if (typeof(IRepresentationList).IsAssignableFrom(type))
is never true when the resource is of type SimpleListRepresentation.

Second issue :
the line
var genericTypeArg = type.GetGenericArguments().Single();
throws an exception because the class that inherits from SimpleListCollection<> isn't itself generic. The generic type should be infered from the base class.

Proposed patch (XmlHalMediaTypeFormatter.cs line 66 - 2 lines changed):
static object ReadHalResource(Type type, XElement xml)
{
Representation representation;

        if (xml == null)
        {
            return null;
        }

        // First, determine if Resource of type Generic List and construct Instance with respective Parameters

        //if (typeof(IRepresentationList).IsAssignableFrom(type))   
        if (type.BaseType.Name == "SimpleListRepresentation`1")     //patch : SimpleListRepresentation isn't an IRepresentationList. My patch is ugly there must be a smarter way to do this check
        {
            var resourceListXml = xml.Elements("resource");  // .Where(x => x.Attribute("rel").Value == "item");
            //var genericTypeArg = type.GetGenericArguments().Single(); 
            var genericTypeArg = type.BaseType.GetGenericArguments().Single();      //patch : use the base class to infer generic argument
            var resourceList = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(genericTypeArg));

            foreach (var resourceListItem in resourceListXml.Select(resourceXml => ReadHalResource(genericTypeArg, resourceXml)))
            {
                resourceList.Add(resourceListItem);
            }

As mentioned, I'm not happy with the check, there must be a cleaner way to achieve that, but it works...

Hope this helps and thanks for you work.

Regards
/Sebastien

CreateUri only supports relative Uris

Currently the CreateUri method only supports creating relative Uris. If my Uri template isn't relative, an exception is thrown. It would be nice to be able to handle absolute Uris.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.