Skip to content

Commit 24d7aa9

Browse files
committed
start on usage docs
1 parent e074367 commit 24d7aa9

12 files changed

+509
-2
lines changed

docs/GET_Articles_Response

-1
This file was deleted.

docs/docfx.json

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"files": [
2424
"getting-started/**.md",
2525
"getting-started/**/toc.yml",
26+
"usage/**.md",
2627
"request-examples/**.md",
2728
"toc.yml",
2829
"*.md"

docs/request-examples/index.md

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
These requests have been generated against the "GettingStarted" application and are updated on every deployment.
44

5+
All of these requests have been created using out-of-the-box features.
6+
57
## Simple CRUD
68

79
### Create

docs/toc.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
- name: Getting Started
22
href: getting-started/
3-
3+
4+
- name: Usage
5+
href: usage/
6+
47
- name: API
58
href: api/
69
homepage: api/index.md

docs/usage/errors.md

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Errors
2+
3+
By default, errors will only contain the properties defined by the `Error` class.
4+
However, you can create your own by inheriting from Error and either throwing it in a `JsonApiException` or returning the error from your controller.
5+
6+
```c#
7+
public class CustomError : Error
8+
{
9+
public CustomError(int status, string title, string detail, string myProp)
10+
: base(status, title, detail)
11+
{
12+
MyCustomProperty = myProp;
13+
}
14+
15+
public string MyCustomProperty { get; set; }
16+
}
17+
```
18+
19+
If you throw a `JsonApiException` that is unhandled, the middleware will properly serialize it and return it as a json:api error.
20+
21+
```c#
22+
public void MyMethod()
23+
{
24+
var error = new CustomError(507, "title", "detail", "custom");
25+
throw new JsonApiException(error);
26+
}
27+
```
28+
29+
You can use the `IActionResult Error(Error error)` method to return a single error message, or you can use the `IActionResult Errors(ErrorCollection errors)` method to return a collection of errors from your controller.
30+
31+
```c#
32+
[HttpPost]
33+
public override async Task<IActionResult> PostAsync([FromBody] MyEntity entity)
34+
{
35+
if(_db.IsFull)
36+
return Error(new CustomError("507", "Database is full.", "Theres no more room.", "Sorry."));
37+
38+
if(model.Validations.IsValid == false)
39+
return Errors(model.Validations.GetErrors());
40+
}
41+
```
42+
43+
## Example: Including Links
44+
45+
This example demonstrates one way you can include links with your error payloads.
46+
47+
This example assumes that there is a support documentation site that provides additional information based on the HTTP Status Code.
48+
49+
```c#
50+
public class LinkableError : Error
51+
{
52+
public LinkableError(int status, string title)
53+
: base(status, title)
54+
{ }
55+
56+
public ErrorLink Links => "https://example.com/errors/" + Status;
57+
}
58+
59+
var error = new LinkableError(401, "You're not allowed to do that.");
60+
throw new JsonApiException(error);
61+
```
62+
63+
64+
65+
66+
67+

docs/usage/filtering.md

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Filtering
2+
3+
Resources can be filtered by attributes using the `filter` query parameter.
4+
By default, all attributes are filterable.
5+
The filtering strategy we have selected, uses the following form.
6+
7+
```
8+
?filter[attribute]=value
9+
```
10+
11+
For operations other than equality, the query can be prefixed with an operation identifier.
12+
Examples can be found in the table below.
13+
14+
| Operation | Prefix | Example |
15+
|-------------------------------|---------------|------------------------------------------|
16+
| Equals | `eq` | `?filter[attribute]=eq:value` |
17+
| Not Equals | `ne` | `?filter[attribute]=ne:value` |
18+
| Less Than | `lt` | `?filter[attribute]=lt:10` |
19+
| Greater Than | `gt` | `?filter[attribute]=gt:10` |
20+
| Less Than Or Equal To | `le` | `?filter[attribute]=le:10` |
21+
| Greater Than Or Equal To | `ge` | `?filter[attribute]=ge:10` |
22+
| Like (string comparison) | `like` | `?filter[attribute]=like:value` |
23+
| In Set | `in` | `?filter[attribute]=in:value1,value2` |
24+
| Not In Set | `nin` | `?filter[attribute]=nin:value1,value2` |
25+
| Is Null | `isnull` | `?filter[attribute]=isnull:` |
26+
| Is Not Null | `isnotnull` | `?filter[attribute]=isnotnull:` |
27+
28+
Filters can be combined and will be applied using an AND operator.
29+
The following are equivalent query forms to get articles whose ordinal values are between 1-100.
30+
31+
```http
32+
GET /api/articles?filter[ordinal]=gt:1,lt:100 HTTP/1.1
33+
Accept: application/vnd.api+json
34+
```
35+
```http
36+
GET /api/articles?filter[ordinal]=gt:1&filter[ordinal]=lt:100 HTTP/1.1
37+
Accept: application/vnd.api+json
38+
```
39+
40+
## Custom Filters
41+
42+
You can customize the filter implementation by overriding the method in the `DefaultEntityRepository`.
43+
44+
```c#
45+
public class AuthorRepository : DefaultEntityRepository<Author>
46+
{
47+
public AuthorRepository(
48+
AppDbContext context,
49+
ILoggerFactory loggerFactory,
50+
IJsonApiContext jsonApiContext)
51+
: base(context, loggerFactory, jsonApiContext)
52+
{ }
53+
54+
public override IQueryable<TEntity> Filter(
55+
IQueryable<TEntity> authors,
56+
FilterQuery filterQuery)
57+
// if the filter key is "query" (filter[query]),
58+
// find Authors with matching first or last names
59+
// for all other filter keys, use the base method
60+
=> filter.Attribute.Is("query")
61+
? authors.Where(a =>
62+
a.First.Contains(filter.Value)
63+
|| a.Last.Contains(filter.Value))
64+
: base.Filter(authors, filter);
65+
}
66+
```
67+

docs/usage/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Usage

docs/usage/meta.md

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Metadata
2+
3+
Non-standard metadata can be added to your API responses in 2 ways. Resource and Request meta. In the event of a key collision, the Request Meta will take precendence.
4+
5+
## Resource Meta
6+
7+
Resource Meta is metadata defined on the resource itself by implementing the `IHasMeta` interface.
8+
9+
```c#
10+
public class Person : Identifiable, IHasMeta
11+
{
12+
public Dictionary<string, object> GetMeta(IJsonApiContext context)
13+
=> new Dictionary<string, object> {
14+
{ "copyright", "Copyright 2018 Example Corp." },
15+
{ "authors", new string[] { "Jared Nance" } }
16+
};
17+
}
18+
```
19+
20+
## Request Meta
21+
22+
Request Meta can be added by injecting a service that implements `IRequestMeta`.
23+
This is useful if you need access to other injected services to build the meta object.
24+
25+
```c#
26+
public class RequestMetaService : IRequestMeta
27+
{
28+
public RequestMetaService(/*...other dependencies here */) {
29+
// ...
30+
}
31+
32+
public Dictionary<string, object> GetMeta(IJsonApiContext context)
33+
=> return new Dictionary<string, object> {
34+
{ "copyright", "Copyright 2018 Example Corp." },
35+
{ "authors", new string[] { "Jared Nance" } }
36+
};
37+
}
38+
```
39+
40+
```json
41+
{
42+
"meta": {
43+
"copyright": "Copyright 2015 Example Corp.",
44+
"authors": [
45+
"Jared Nance"
46+
]
47+
},
48+
"data": {
49+
// ...
50+
}
51+
}
52+
```

docs/usage/options.md

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Global Options
2+
3+
Configuration can be applied when adding the services to the DI container.
4+
5+
```c#
6+
public IServiceProvider ConfigureServices(IServiceCollection services) {
7+
services.AddJsonApi<AppDbContext>(options => {
8+
// configure the options here
9+
});
10+
}
11+
```
12+
13+
## Client Generated Ids
14+
15+
By default, the server will respond with a 403 Forbidden HTTP Status Code if a POST request is received with a client generated id.
16+
17+
However, this can be allowed by setting the AllowClientGeneratedIds flag in the options
18+
19+
```c#
20+
services.AddJsonApi<AppDbContext>(options => {
21+
options.AllowClientGeneratedIds = true;
22+
});
23+
```
24+
25+
## Pagination
26+
27+
If you would like pagination implemented for all resources, you can specify a default page size.
28+
29+
You can also include the total number of records in each request. Note that when using this feature, it does add some query overhead since we have to also request the total number of records.
30+
31+
```c#
32+
services.AddJsonApi<AppDbContext>(options => {
33+
options.DefaultPageSize = 10;
34+
});
35+
```
36+
37+
## Relative Links
38+
39+
All links are absolute by default. However, you can configure relative links.
40+
41+
```c#
42+
services.AddJsonApi<AppDbContext>(options => {
43+
options.RelativeLinks = true;
44+
});
45+
```
46+
47+
```json
48+
{
49+
"type": "articles",
50+
"id": "4309",
51+
"relationships": {
52+
"author": {
53+
"links": {
54+
"self": "/api/v1/articles/4309/relationships/author",
55+
"related": "/api/v1/articles/4309/author"
56+
}
57+
}
58+
}
59+
}
60+
```
61+
62+
## Custom Query Parameters
63+
64+
If you would like to use custom query params (parameters not reserved by the json:api specification), you can set AllowCustomQueryParameters = true. The default behavior is to return an HTTP 400 Bad Request for unknown query parameters.
65+
66+
```c#
67+
services.AddJsonApi<AppDbContext>(options => {
68+
options.AllowCustomQueryParameters = true;
69+
});
70+
```
71+
72+
## Custom Serializer Settings
73+
74+
We use Newtonsoft.Json for all serialization needs.
75+
If you want to change the default serializer settings, you can:
76+
77+
```c#
78+
options.SerializerSettings = new JsonSerializerSettings()
79+
{
80+
NullValueHandling = NullValueHandling.Ignore,
81+
ContractResolver = new DasherizedResolver()
82+
}
83+
```

docs/usage/resource-graph.md

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# The Resource Graph
2+
3+
_NOTE: prior to 3.0.0 this was called the `ContextGraph`_
4+
5+
The `ResourceGraph` is a map of all the json:api resources and their relationships that your API serves.
6+
7+
It is built at app startup and available as a singleton through Dependency Injection.
8+
9+
## Constructing The Graph
10+
11+
### Entity Framework
12+
13+
If you are using Entity Framework Core as your ORM, you can add an entire `DbContext` with one line.
14+
15+
```c#
16+
// Startup.cs
17+
public void ConfigureServices(IServiceCollection services)
18+
{
19+
services.AddJsonApi<AppDbContext>();
20+
}
21+
```
22+
23+
### Defining Non-EF Resources
24+
25+
If you have resources that are not members of a DbContext, you can manually add them to the graph.
26+
27+
```c#
28+
// Startup.cs
29+
public void ConfigureServices(IServiceCollection services)
30+
{
31+
var mvcBuilder = services.AddMvc();
32+
33+
services.AddJsonApi(options => {
34+
options.BuildResourceGraph((builder) => {
35+
// MyModel is the internal type
36+
// "my-models" is the type alias exposed through the API
37+
builder.AddResource<MyModel>("my-models");
38+
});
39+
}, mvcBuilder);
40+
}
41+
```
42+
43+
### Resource Names
44+
45+
If a `DbContext` is specified when adding the services, the context will be used to define the resources and their names. By default, these names will be hyphenated.
46+
47+
```c#
48+
public class AppDbContext : DbContext {
49+
// this will be translated into "my-models"
50+
public DbSet<MyModel> MyModels { get; set; }
51+
}
52+
```
53+
54+
You can also specify your own resource name.
55+
56+
```c#
57+
public class AppDbContext : DbContext {
58+
// this will be translated into "someModels"
59+
[Resource("someModels")]
60+
public DbSet<MyModel> MyModels { get; set; }
61+
}
62+
```
63+
64+
65+
66+
67+

0 commit comments

Comments
 (0)