Giter Site home page Giter Site logo

njsonapi's Introduction

NJsonApi

The .NET server implementation of the {json:api} standard.

The library is in the process of upgrading to spec version 1.0. Some features are not yet compatibile with the current version of spec!

Spawned as an internal project used in production environment, the package is now available in the open thanks to courtesy of SocialCee! Further development, including updating to {json:api} 1.0 will take place here.

Quick start

After installing the package:

PM> Install-Package NJsonApi

... given the two POCOs:

    public class World
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<Continent> Continents { get; set; }
    }
	
	public class Continent
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public World World { get; set; }
        public int WorldId { get; set; }
    }

... and the NJsonApi mapping and bootstrapping:

	var configBuilder = new ConfigurationBuilder();

	configBuilder
		.Resource<World>()
		.WithAllProperties()
		.WithLinkTemplate("/worlds/{id}");
		
	configBuilder
		.Resource<Continent>()
		.WithAllProperties()
		.WithLinkTemplate("/continents/{id}");

	var nJsonApiConfig = configBuilder.Build();
	nJsonApiConfig.Apply(httpConfiguration);

... a standard Web API controller method:

	[HttpGet, Route]
	public IEnumerable<World> Get()
	{
		return new List<World>() { ... };
	}

... starts returning the {json:api} compliant JSON:

{
  "data": {
    "id": "1",
    "type": "worlds",
    "attributes": {
      "name": "Hello"
    },
    "relationships": {
      "continents": {
        "data": [
          {
            "id": "1",
            "type": "continents"
          },
          {
            "id": "2",
            "type": "continents"
          },
          {
            "id": "3",
            "type": "continents"
          }
        ],
        "meta": {
          "count": "3"
        }
      }
    },
    "links": {
      "self": "http://localhost:56827/worlds/1"
    }
  },
  "included": [
    {
      "id": "1",
      "type": "continents",
      "attributes": {
        "name": "Hello Europe",
        "worldId": 1
      },
      "relationships": {
        "world": {
          "data": {
            "id": "1",
            "type": "worlds"
          }
        }
      },
      "links": {
        "self": "http://localhost:56827/continents/1"
      }
    },
    {
      "id": "2",
      "type": "continents",
      "attributes": {
        "name": "Hello America",
        "worldId": 1
      },
      "relationships": {
        "world": {
          "data": {
            "id": "1",
            "type": "worlds"
          }
        }
      },
      "links": {
        "self": "http://localhost:56827/continents/2"
      }
    },
    {
      "id": "3",
      "type": "continents",
      "attributes": {
        "name": "Hello Asia",
        "worldId": 1
      },
      "relationships": {
        "world": {
          "data": {
            "id": "1",
            "type": "worlds"
          }
        }
      },
      "links": {
        "self": "http://localhost:56827/continents/3"
      }
    }
  ]
}

Sample project included

Play around with the working NJsonApi.HelloWorld sample project.

Use a browser and explore the related resource thanks to HATEOAS...

... or run Fiddler and load the included session file for a full test bench.

njsonapi's People

Contributors

jacek-gorgon 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

njsonapi's Issues

IRelationshipMapping templates are never set

In IRelationshipMapping there are two templates (SelfUrlTemplate and RelatedUrlTemplate) for generating links within the relationships such as http://localhost:56827/worlds/1/relationships/continents below:

{
  "data": {
    "id": "1",
    "type": "worlds",
    "attributes": {
      "name": "Hello"
    },
    "relationships": {
      "continents": {
        "links":[
           "self" : "http://localhost:56827/worlds/1/relationships/continents",
           "related": "http://localhost:56827/worlds/1/continents",
        ],
        "data": [
          {
            "id": "1",
            "type": "continents"
          },
          {
            "id": "2",
            "type": "continents"
          },
          {
            "id": "3",
            "type": "continents"
          }
        ],
        "meta": {
          "count": "3"
        }
      }
    },
    "links": {
      "self": "http://localhost:56827/worlds/1"
    }
  },

There is no way to set these relationship Urls. It should probably be done through the ConfigurationBuilder so that the relationship maps are set at application start.

I appreciate if covered by "Not fully 1.0 compliant" but thought it wise to mention for anyone else wondering where the relationship links are. I will be implementing this in the net-core-1 branch, although I think that branch is quite a long way ahead.

Flatten included collection

The current implementation groups included resources by type. 1.0 requires a flat list of resources of all types (each having a type attribute).

Add attributes sub-object

Currently, "simple" attributes are serialized directly on the object representation root. Refactor this to be included on a sub-object "Attributes" as per 1.0 spec.

Class with a property that does not have a set causes crash on startup

Class with a property that does not have a set

public class Test
{
    public int Id { get; set; }
    public DateTime? VerifiedUtc { get; set; }

    // Causes crash in library
    public bool IsVerified { get { return (VerifiedUtc != null); } }
}

Causes crash on the following lines when starting up.

configBuilder
    .Resource<Test>()
    .WithAllProperties()
    .WithLinkTemplate("/tests/{id}");

An exception of type 'System.ArgumentNullException' occurred in System.Core.dll but was not handled in user code

Value cannot be null. Parameter name: method

at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable`1 arguments)
at NJsonApi.ResourceConfigurationBuilder`1.AddProperty(PropertyInfo propertyInfo, Type type, SerializationDirection direction)
at NJsonApi.ResourceConfigurationBuilder`1.WithAllSimpleProperties()
at NJsonApi.ResourceConfigurationBuilder`1.WithAllProperties()

I can understand not supporting it, but would be great to have a clear error message with property name and why, to make debugging easier.

Refactor IIS and OWIN integration

Currently, the implementation assumes the solution is hosted using traditional asp.net in an IIS container. There is a fallback that will limit functionality (but not throw) if hosted in OWIN.

Refactor code so that IIS and OWIN integration are separate packages and users can opt for either. Still, the core should be as agnostic as possible, so extracted integration parts should be as small as possible

Message "Object reference not set to an instance of an object." string

at NJsonApi.Serialization.TransformationHelper.CreateRelationships(Object objectGraph, String parentId, IResourceMapping resourceMapping, Context context) at NJsonApi.Serialization.TransformationHelper.CreateResourceRepresentation(Object objectGraph, IResourceMapping resourceMapping, Context context) at NJsonApi.Serialization.JsonApiTransformer.<>c__DisplayClass8_0.<Transform>b__0(Object o) at System.Linq.Enumerable.WhereSelectListIterator2.MoveNext()
at System.Linq.Enumerable.Single[TSource](IEnumerable1 source) at NJsonApi.Serialization.TransformationHelper.ChooseProperResourceRepresentation(Object resource, IEnumerable1 representationList)
at NJsonApi.Serialization.JsonApiTransformer.Transform(Object objectGraph, Context context)
at JsonApiHelpers.Formatters.EnhancedJsonApiFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content) in C:\JsonApiHelpers\Formatters\EnhancedJsonApiFormatter.cs:line 53
at System.Net.Http.Formatting.BufferedMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content, CancellationToken cancellationToken)
at System.Net.Http.Formatting.BufferedMediaTypeFormatter.WriteToStreamSync(Type type, Object value, Stream writeStream, HttpContent content, CancellationToken cancellationToken)
at System.Net.Http.Formatting.BufferedMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)`

What is the reason of this exception?

What is left to implement JSON API 1.0?

I want to use NJsonApi and I would like to know what the remaining tasks are to match JSON API 1.0. I have noticed a couple of non-conformities but I am new to the implementation and the spec. Is there anything I'm missing?

For example, relationships do not include links:

 {
  "data": [
    {
      "id": "1",
      "type": "worlds",
      "attributes": {
        "name": "Hello"
      },
      "relationships": {
        "continents": {
          "links": {
            "self": "http://localhost:56827/continents"
          },
          "data": [] 
         // Snip

Secondly, JSON API says that "Attributes may contain any valid JSON value." and I think if the property of the type being returned is complex (not string/int etc) then NJsonApi will assume that it has a resource associated with it.

Error block returned incorrectly

The error block within the json is returning the following:

{
  "errors": {
    "error": {
      "Id": "f2282c1a-c586-4dee-9c9a-f43e31b7caac",
      "Status": "404",
      "Title": "Exception of type 'Api.Controllers.OoopsException' was thrown.",
      "Detail": null,
      "Href": null
    }
  }
}

The JSON API specification (http://jsonapi.org/format/#errors) says it should return:

{
  "errors": [
    {
      "id": "f2282c1a-c586-4dee-9c9a-f43e31b7caac",
      "links": { "about": "http://details.to/errors/112" },
      "status": "400",
      "code": 112,
      "source": { "pointer": "/data/attributes/first-name", "parameter": "name" },
      "title":  "Invalid Attribute",
      "detail": "First name must contain at least three characters.",
      "meta": { "fintech": "doesn't exist" }
    },
    {
      "id": "6b6c6eee-70eb-4950-9f5a-9fd2e9ceea4c",
      "links": { "about": "http://details.to/errors/113" },
      "status": "400",
      "code": 113,
      "source": { "pointer": "/data/attributes/last-name", "parameter": "name" },
      "title":  "Invalid Attribute",
      "detail": "Last name must contain at least three characters.",
    },
  ]
}

For more examples see:- http://jsonapi.org/examples/#error-objects

Seems that we could just change Errors within CompoundDocument to be an array instead of a Dictionary: https://github.com/jacek-gorgon/NJsonApi/blob/master/NJsonApi/Serialization/Representations/Documents/CompoundDocument.cs#L32

There would obviously be more work required to be able to support multiple errors and push that amount of details back to the client.

I'll try to get a PR together to give examples.

Is there a deserializer that can be used for the client side?

I've been using NJsonApi for serializing objects on the server side and its been working great so far.

I'm just wondering, do you guys have any deserializer that would work in deserializing the content from a C# client?

I wasn't able to find one when I took a look at the code. My guess is we could use the JsonApiTransformer to transform the Json back into a strongly typed object?

Add Actions to representation

Hey!
Is it possible to add a "Action" section where it would include possible HTTP methods to be applied to the represented resource and correspondent href to execute the action.
If yes, how could it be implemented?

Thanks!

POST to Worlds is not de-serialising properly

Running on the master branch, I post the following as per the spec:

POST localhost:56827/worlds
Content-Type: application/vnd.api+json

{
    "data" : 
    {
      "type" : "worlds",
      "attributes": {
        "name": "A New World Number 2",
      },
    }
}

The response is:

{
  "data": {
    "id": "3",
    "type": "worlds",
    "attributes": {
      "name": null
    },
    "links": {
      "self": "http://localhost:56827/worlds/3"
    }
  },
  "included": []
}

The name of the new world has not been set.

I believe the cause is in NJsonApi.Serialization.JsonApiTransformer.TransformBack(...), line 81:

var resource = updateDocument.Data[resourceKey] as JObject;
if (resource == null)
{
    return delta;
}

The expected resourceKey is "worlds" but the provided key is "data".

I'll be fixing this in my .NET Core 1.0 branch but I found that it applies to this master too. I've changed a lot of code, so I doubt a merge of my fix will be possible.

Example for posting content appears to be wrong

The Hello Worlds project fiddler file has the following packet of data being sent.

"worlds": [
    {
      "href": "http://localhost:56827/worlds/1",
      "id": "1",
      "type": "worlds",
      "name": "Hello",
      "links": {
        "continents": [
          "1",
          "2",
          "3"
        ]
      }
    }

However this doesn't appear to match the spec http://jsonapi.org/format/#crud

POST /photos HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": {
    "type": "photos",
    "attributes": {
      "title": "Ember Hamster",
      "src": "http://example.com/images/productivity.png"
    },
    "relationships": {
      "photographer": {
        "data": { "type": "people", "id": "9" }
      }
    }
  }
}

I can't get the correct format to work, so I'm assuming it's not supported at this stage?

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.