-
Notifications
You must be signed in to change notification settings - Fork 11
Overview
The purpose of this section is to present a high-level overview of the JsonApiFramework.
The following diagram demonstrates a standard HTTP GET request of a JSON API document between a client application and hypermedia API server and how JsonApiFramework could be utilized on both the client-side and server-side to make this interaction effortless with further details on the following top-level JsonApiFramework concepts:
- Service Model
- Document
- Document Context
- Reading JSON API Documents
- Building/Writing JSON API Documents
The ServiceModel
represents the domain model of the resources produced and consumed by the hypermedia API server or client application. The ServiceModel
contains the metadata of individual resources and the schema of how resources are related to each other through relationships (to-one or to-many).
The term resource is used here to represent and mean both the JSON API resource and .NET CLR resource.
The ServiceModel
is used by a DocumentContext
in the realization of the following services:
- Conversion between JSON API resources and CLR resources
- Generation of JSON API standard hypermedia for JSON API links and relationships
See XXX to learn more.
The following is an example Blogging domain model of CLR resources that could be represented by a ServiceModel
.
namespace Blogging
{
public class Blog
{
public long BlogId { get; set; }
public string Name { get; set; }
}
public class Article
{
public long ArticleId { get; set; }
public string Title { get; set; }
public string Text { get; set; }
}
public class Comment
{
public long CommentId { get; set; }
public string Body { get; set; }
}
public class Person
{
public long PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Twitter { get; set; }
}
}
The ServiceModel
is configured by developers for the respective domain model for the system under consideration. A ServiceModel
is configured in any of the following ways:
- Built inside a derived
DocumentContext
by overriding theOnServiceModelCreating
method. - Built outside a derived
DocumentContext
and configured for use by overriding theOnConfiguring
method. - Built outside a
DocumentContext
and passed in the constructor of aDocumentContext
.
ServiceModel
configuration can be supplemented by the use of conventions thus making the configuration of the ServiceModel convention-based as desired by the developer.
Any developer explicit configuration will always take precedence over any convention-based configuration.
The following is an example of a ServiceModel
configuration with explicit configuration of the resource types for the previous Blogging domain model of resources.
namespace Blogging
{
public class BlogConfiguration : ResourceTypeBuilder<Blog>
{
public BlogConfiguration()
{
// Hypermedia
this.Hypermedia()
.SetApiCollectionPathSegment("blogs");
// ResourceIdentity
this.ResourceIdentity(x => x.BlogId)
.SetApiType("blogs");
// Attributes
this.Attribute(x => x.Name)
.SetApiPropertyName("name");
// Relationships
this.ToManyRelationship<Article>("articles")
.SetApiRelPathSegment("articles");
}
}
public class ArticleConfiguration : ResourceTypeBuilder<Article>
{
public ArticleConfiguration()
{
// Hypermedia
this.Hypermedia()
.SetApiCollectionPathSegment("articles");
// ResourceIdentity
this.ResourceIdentity(x => x.ArticleId)
.SetApiType("articles");
// Attributes
this.Attribute(x => x.Title)
.SetApiPropertyName("title");
this.Attribute(x => x.Text)
.SetApiPropertyName("text");
// Relationships
this.ToOneRelationship<Person>("author")
.SetApiRelPathSegment("author");
this.ToManyRelationship<Comment>("comments")
.SetApiRelPathSegment("comments");
}
}
public class CommentConfiguration : ResourceTypeBuilder<Comment>
{
public CommentConfiguration()
{
// Hypermedia
this.Hypermedia()
.SetApiCollectionPathSegment("comments");
// ResourceIdentity
this.ResourceIdentity(x => x.Id)
.SetApiType("comments");
// Attributes
this.Attribute(x => x.Body)
.SetApiPropertyName("body");
// Relationships
this.ToOneRelationship<Person>("author")
.SetApiRelPathSegment("author");
}
}
public class PersonConfiguration : ResourceTypeBuilder<Person>
{
public PersonConfiguration()
{
// Hypermedia
this.Hypermedia()
.SetApiCollectionPathSegment("people");
// ResourceIdentity
this.ResourceIdentity(x => x.Id)
.SetApiType("people");
// Attributes
this.Attribute(x => x.FirstName)
.SetApiPropertyName("first-name");
this.Attribute(x => x.LastName)
.SetApiPropertyName("last-name");
this.Attribute(x => x.Twitter)
.SetApiPropertyName("twitter");
// Relationships
this.ToManyRelationship<Article>("articles")
.SetApiRelPathSegment("articles");
this.ToManyRelationship<Comment>("comments")
.SetApiRelPathSegment("comments");
}
}
}
The following is an example of a ServiceModel
convention-based configuration for the previous Blogging domain model of CLR resources with the help of the following conventions:
- Convention for JSON API
type
to be pluralized from the C# type name and use the JSON API standard for member names (lower-cased and hyphenated) - Convention for JSON API
attributes
to use the JSON API standard for member names (lower-cased and hyphenated) - Convention for auto-discovery of all public properties as JSON API
attributes
With these conventions the only thing left to configure is the relationships between resources.
namespace Blogging
{
public class BlogConfiguration : ResourceTypeBuilder<Blog>
{
public BlogConfiguration()
{
// Relationships
this.ToManyRelationship<Article>("articles");
}
}
public class ArticleConfiguration : ResourceTypeBuilder<Article>
{
public ArticleConfiguration()
{
// Relationships
this.ToOneRelationship<Person>("author");
this.ToManyRelationship<Comment>("comments");
}
}
public class CommentConfiguration : ResourceTypeBuilder<Comment>
{
public CommentConfiguration()
{
// Relationships
this.ToOneRelationship<Person>("author");
}
}
public class PersonConfiguration : ResourceTypeBuilder<Person>
{
public PersonConfiguration()
{
// Relationships
this.ToManyRelationship<Article>("articles");
this.ToManyRelationship<Comment>("comments");
}
}
}
The Document
represents a JSON API document from the JSON API specification. The Document
is the top-level class for a package of classes that represent the domain model of the JSON API specification.
The JsonApiFramework abstracts any object that can serialize/deserialize itself to/from JSON respectively with the JsonObject
class. Document
uses the services JsonObject
provides to serialize/deserialize itself to/from JSON respectively.
JsonApiFramework uses the JSON.NET library for the actual implementation of serialization and deserialization between JSON and C# objects.
var json = document.ToJson();
var document = JsonObject.Parse<Document>(json);
The DocumentContext
represents a session with a JSON API document for reading and writing of CLR resources. The DocumentContext
uses a ServiceModel
that represents the domain model of the resources produced and consumed by the hypermedia API server or client application for the realization of the following services:
- Conversion between JSON API resources and CLR resources
- Generation of JSON API standard hypermedia for JSON API links and relationships
This is an example of a derived DocumentContext
class for the Blogging domain model of CLR resources where the ServiceModel
is configured internal to the derived DocumentContext
class with conventions and explicit configurations defined earlier.
namespace Blogging
{
public class BloggingDocumentContext : DocumentContext
{
protected override void OnConventionSetCreating(IConventionSetBuilder conventionSetBuilder)
{
Contract.Requires(conventionSetBuilder != null);
conventionSetBuilder.ApiTypeNamingConventions()
.AddPluralNamingConvention()
.AddStandardMemberNamingConvention();
conventionSetBuilder.ApiAttributeNamingConventions()
.AddStandardMemberNamingConvention();
conventionSetBuilder.ResourceTypeConventions()
.AddPropertyDiscoveryConvention();
}
protected override void OnServiceModelCreating(IServiceModelBuilder serviceModelBuilder)
{
Contract.Requires(serviceModelBuilder != null);
serviceModelBuilder.Configurations.Add(new ArticleConfiguration());
serviceModelBuilder.Configurations.Add(new BlogConfiguration());
serviceModelBuilder.Configurations.Add(new CommentConfiguration());
serviceModelBuilder.Configurations.Add(new PersonConfiguration());
}
}
}
CLR resources are read from the JSON API document using a DocumentContext
object. Reading CLR resources from JSON API documents is the same for both client-side and server-side development.
See XXX to learn more.
This is an example of using the previous Blogging DocumentContext
object to read both document-level and resource-level things about the JSON API document.
// Deserialize JSON into JSON API document.
var document = JsonObject.Parse<Document>(json);
// Read JSON API document.
using (var context = new BloggingContext(document))
{
// Read document-level things.
var documentType = context.GetDocumentType();
var documentJsonApi = context.GetDocumentJsonApi();
var documentMeta = context.GetDocumentMeta();
var documentLinks = context.GetDocumentLinks();
// Read resource-level things.
// .. blog
var blog = context.GetResourceCollection<Blog>()
.SingleOrDefault(blog => blog.Name == "JsonApiFramework");
Assert(blog != null);
var blogLinks = context.GetResourceLinks(blog);
var blogRelationships = context.GetResourceRelationships(blog);
// .. blog related articles
Assert(blogRelationships != null);
var blogToArticlesRelationship = blogRelationships.GetRelationship("articles");
Assert(blogToArticlesRelationship != null);
var blogToArticles = context.GetRelatedToManyResourceCollection<Article>(blogToArticlesRelationship)
.ToList();
}
JSON API documents are created with CLR resources using a DocumentContext
object. Writing JSON API documents with CLR resources is different for both client-side and server-side development in the context of the JSON API specification.
See XXX to learn more.
This is an example of using the previous Blogging DocumentContext
object to build and write JSON with framework generated hypermedia for a hypermedia API server request for the handling of a HTTP GET request.
var currentRequestUrl = HttpContext.Current.Request.Url;
using (var context = new BloggingContext())
{
// Build/Write JSON API document.
var document = context
.NewDocument(currentRequestUrl)
// Document links
.Links()
.AddSelfLink()
.LinksEnd()
// Convert CLR Blog resource to JSON API resource
.Resource(blog)
// Blog relationships
.Relationships()
.AddRelationship("articles", Keywords.Self, Keywords.Related)
.RelationshipsEnd()
// Blog links
.Links()
.AddSelfLink()
.LinksEnd()
.ResourceEnd()
.Included()
// Convert related "to-many" CLR Article resources to JSON API resources
// Automatically generate "to-many" resource linkage in blog to related articles
.ToMany(blog, "articles", articles)
// Article relationships
.Relationships()
.AddRelationship("author", Keywords.Self, Keywords.Related)
.RelationshipsEnd()
// Article links
.Links()
.AddSelfLink()
.LinksEnd()
.ToManyEnd()
.IncludedEnd()
.WriteDocument();
// Serialize JSON API document into JSON.
var json = document.ToJson();
return json;
}
This is an example of using the previous Blogging DocumentContext
object to build and write JSON for a client application request to create a resource with a HTTP POST request.
using (var context = new BloggingContext())
{
// Build/Write JSON API document.
var document = context
.NewDocument()
Resource<Article>()
.Attributes()
.AddAttribute(article => article.Title, "JsonApiFramework")
.AddAttribute(article => article.Text,
"JsonApiFramework is a fast, extensible, and portable C#/.NET framework for the reading and writing "
"of client-side and server-side JSON API documents based on the domain model of the hypermedia API resources.")
.AttributesEnd()
.ResourceEnd()
.WriteDocument();
// Serialize JSON API document into JSON.
var json = document.ToJson();
return json;
}