Skip to content

Commit eb9441f

Browse files
committed
migrate remaining docs
1 parent 0d853a4 commit eb9441f

19 files changed

+748
-18
lines changed

docs/getting-started/intro.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

docs/getting-started/step-by-step.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ The shortest path to a running API looks like:
1010
- Define controllers
1111
- Add Middleware and Services
1212
- Seed the database
13-
- Run Migrations
1413
- Start the app
1514

1615
This page will walk you through the **simplest** use case. More detailed examples can be found in the detailed usage subsections.
@@ -128,13 +127,6 @@ public void Configure(
128127
}
129128
```
130129

131-
### Run Migrations
132-
133-
```
134-
dotnet ef migrations add AddPeople
135-
dotnet ef database update
136-
```
137-
138130
### Start the App
139131

140132
```

docs/getting-started/toc.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
- name: Introduction
2-
href: intro.md
3-
41
- name: Installation
52
href: install.md
63

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Controllers
2+
3+
You need to create controllers that inherit from `JsonApiController<TEntity>`
4+
5+
```c#
6+
public class ArticlesController : JsonApiController<Article>
7+
{
8+
public ArticlesController(
9+
IJsonApiContext jsonApiContext,
10+
IResourceService<Article> resourceService,
11+
ILoggerFactory loggerFactory)
12+
: base(jsonApiContext, resourceService, loggerFactory)
13+
{ }
14+
}
15+
```
16+
17+
## Non-Integer Type Keys
18+
19+
If your model is using a type other than int for the primary key, you must explicitly declare it in the controller and service generic type definitions.
20+
21+
```c#
22+
public class ArticlesController : JsonApiController<Article, Guid>
23+
//---------------------- ^^^^
24+
{
25+
public ArticlesController(
26+
IJsonApiContext jsonApiContext,
27+
IResourceService<Article, Guid> resourceService,
28+
//--------------------- ^^^^
29+
30+
ILoggerFactory loggerFactory)
31+
: base(jsonApiContext, resourceService, loggerFactory)
32+
{ }
33+
}
34+
```
35+
36+
## Resource Access Control
37+
38+
It is often desirable to limit what methods are exposed on your controller. The first way, you can do this is to simply inherit from `BaseJsonApiController` and explicitly declare what methods are available.
39+
40+
In this example, if a client attempts to do anything other than GET a resource, an HTTP 404 Not Found response will be returned since no other methods are exposed.
41+
42+
This approach is ok, but introduces some boilerplate that can easily be avoided.
43+
44+
```c#
45+
public class ArticlesController : BaseJsonApiController<Article>
46+
{
47+
public ArticlesController(
48+
IJsonApiContext jsonApiContext,
49+
IResourceService<Article> resourceService)
50+
: base(jsonApiContext, resourceService)
51+
{ }
52+
53+
[HttpGet]
54+
public override async Task<IActionResult> GetAsync()
55+
=> await base.GetAsync();
56+
57+
[HttpGet("{id}")]
58+
public override async Task<IActionResult> GetAsync(TId id)
59+
=> await base.GetAsync(id);
60+
}
61+
```
62+
63+
## Using ActionFilterAttributes
64+
65+
The next option is to use the ActionFilterAttributes that ship with the library. The available attributes are:
66+
67+
- `NoHttpPost`: disallow POST requests
68+
- `NoHttpPatch`: disallow PATCH requests
69+
- `NoHttpDelete`: disallow DELETE requests
70+
- `HttpReadOnly`: all of the above
71+
72+
Not only does this reduce boilerplate, but it also provides a more meaningful HTTP response code.
73+
An attempt to use one blacklisted methods will result in a HTTP 405 Method Not Allowed response.
74+
75+
```c#
76+
[HttpReadOnly]
77+
public class ArticlesController : BaseJsonApiController<Article>
78+
{
79+
public ArticlesController(
80+
IJsonApiContext jsonApiContext,
81+
IResourceService<Article> resourceService)
82+
: base(jsonApiContext, resourceService)
83+
{ }
84+
}
85+
```
86+
87+
## Implicit Access By Service Injection
88+
89+
Finally, you can control the allowed methods by supplying only the available service implementations. In some cases, resources may be an aggregation of entities or a view on top of the underlying entities. In these cases, there may not be a writable IResourceService implementation. In these cases, simply inject the implementation that is available.
90+
91+
As with the ActionFilterAttributes, if a service implementation is not available to service a request, HTTP 405 Method Not Allowed will be returned.
92+
93+
For more information about resource injection, see the next section titled Resource Services.
94+
95+
```c#
96+
public class ReportsController : BaseJsonApiController<Report>
97+
{
98+
public ReportsController(
99+
IJsonApiContext jsonApiContext,
100+
IGetAllService<Report> getAll)
101+
: base(jsonApiContext, getAll: getAll)
102+
{ }
103+
104+
[HttpGet]
105+
public override async Task<IActionResult> GetAsync()
106+
=> await base.GetAsync();
107+
}
108+
```
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Custom Query Formats
2+
3+
For information on the default query parameter formats, see the documentation for each query method.
4+
5+
In order to customize the query formats, you need to implement the `IQueryParser` interface and inject it.
6+
7+
```c#
8+
services.AddScoped<IQueryParser, FooQueryParser>();
9+
```
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Layer Overview
2+
3+
By default, data retrieval is distributed across 3 layers:
4+
5+
JsonApiController (required)
6+
7+
└── EntityResourceService: IResourceService
8+
9+
└── DefaultEntityRepository: IEntityRepository
10+
11+
Customization can be done at any of these layers. However, it is recommended that you make your customizations at the service or the repository layer when possible to keep the controllers free of unnecessary logic.
12+
You can use the following as a general rule of thumb for where to put business logic:
13+
14+
- `Controller`: simple validation logic that should result in the return of specific HTTP status codes such as model validation
15+
- `IResourceService`: advanced BL and replacement of data access mechanisms
16+
- `IEntityRepository`: custom logic that builds on the EF APIs, such as Authorization of data
17+
18+
## Replacing Services
19+
20+
**Note:** If you are using auto-discovery, services will be automatically registered for you.
21+
22+
Replacing services is done on a per resource basis and can be done through simple DI in your Startup.cs file.
23+
24+
In v3.0.0 we introduced an extenion method that you should use to
25+
register services. This method handles some of the common issues
26+
users have had with service registration.
27+
28+
```c#
29+
// Startup.cs
30+
public IServiceProvider ConfigureServices(IServiceCollection services)
31+
{
32+
// custom service
33+
services.AddResourceService<FooService>();
34+
35+
// custom repository
36+
services.AddScoped<AFooRepository>();
37+
}
38+
```
39+
40+
Prior to v3.0.0 you could do it like so:
41+
42+
```c#
43+
// Startup.cs
44+
public IServiceProvider ConfigureServices(IServiceCollection services)
45+
{
46+
// custom service
47+
services.AddScoped<IEntityRepository<Foo>, FooService>();
48+
49+
// custom repository
50+
services.AddScoped<IEntityRepository<Foo>, FooService>();
51+
52+
// ...
53+
}
54+
```
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Middleware
2+
Add the following to your Startup.ConfigureServices method. Replace AppDbContext with your DbContext.
3+
4+
```c3
5+
services.AddJsonApi<AppDbContext>();
6+
```
7+
8+
Add the middleware to the Startup.Configure method. Note that under the hood, this will call app.UseMvc() so there is no need to add that as well.
9+
10+
```c3
11+
app.UseJsonApi();
12+
```
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Entity Repositories
2+
3+
If you want to use EF, but need additional data access logic (such as authorization), you can implement custom methods for accessing the data by creating an implementation of IEntityRepository<Entity, TId>. If you only need minor changes you can override the methods defined in DefaultEntityRepository<TEntity, TId>.
4+
5+
The repository should then be add to the service collection in Startup.cs.
6+
7+
```c#
8+
public IServiceProvider ConfigureServices(IServiceCollection services)
9+
{
10+
services.AddScoped<IEntityRepository<Article>, AuthorizedArticleRepository>();
11+
// ...
12+
}
13+
```
14+
15+
A sample implementation that performs data authorization might look like this.
16+
17+
All of the methods in the DefaultEntityRepository will use the Get() method to get the DbSet<TEntity> so this is a good method to apply scoped filters such as user or tenant authorization.
18+
19+
```c#
20+
public class AuthorizedArticleRepository
21+
: DefaultEntityRepository<Article>
22+
{
23+
private readonly IAuthenticationService _authenticationService;
24+
25+
public AuthorizedArticleRepository(
26+
ILoggerFactory loggerFactory,
27+
IJsonApiContext jsonApiContext,
28+
IDbContextResolver contextResolver,
29+
IAuthenticationService authenticationService)
30+
: base(loggerFactory, jsonApiContext, contextResolver)
31+
{
32+
_authenticationService = authenticationService;
33+
}
34+
35+
public override IQueryable<MyEntity> Get()
36+
=> base.Get()
37+
.Where(e =>
38+
e.UserId == _authenticationService.UserId
39+
);
40+
}
41+
```
42+
43+
## Multiple DbContexts
44+
45+
If you need to use multiple EF DbContext, first add each DbContext to the ContextGraphBuilder.
46+
47+
Then, create an implementation of IDbContextResolver for each context.
48+
49+
Register each of the new IDbContextResolver implementations in the Startup.
50+
51+
You can then create a general repository for each context and inject it per resource type. This example shows a single DbContextARepository for all entities that are members of DbContextA.
52+
53+
Then inject the repository for the correct entity, in this case Foo is a member of DbContextA.
54+
55+
```c#
56+
// Startup.cs
57+
services.AddJsonApi(options => {
58+
options.BuildContextGraph((builder) =>
59+
{
60+
// Add both contexts using the builder
61+
builder.AddDbContext<DbContextA>();
62+
builder.AddDbContext<DbContextB>();
63+
});
64+
}, mvcBuilder);
65+
66+
67+
public class DbContextAResolver : IDbContextResolver
68+
{
69+
private readonly DbContextA _context;
70+
71+
public DbContextAResolver(DbContextA context)
72+
{
73+
_context = context;
74+
}
75+
76+
public DbContext GetContext() => _context;
77+
}
78+
79+
80+
// Startup.cs
81+
services.AddScoped<DbContextAResolver>();
82+
services.AddScoped<DbContextBResolver>();
83+
84+
85+
public class DbContextARepository<TEntity>
86+
: DefaultEntityRepository<TEntity> where TEntity : class, IIdentifiable<T>
87+
{
88+
public DbContextARepository(
89+
ILoggerFactory loggerFactory,
90+
IJsonApiContext jsonApiContext,
91+
DbContextAResolver contextResolver)
92+
: base(loggerFactory, jsonApiContext, contextResolver)
93+
{ }
94+
}
95+
96+
97+
// Startup.cs
98+
services.AddScoped<IEntityRepository<Foo>, DbContextARepository<Foo>>();
99+
```

0 commit comments

Comments
 (0)