Skip to content

Commit 39eae10

Browse files
committed
Add FAQ
1 parent 714b09a commit 39eae10

File tree

3 files changed

+122
-5
lines changed

3 files changed

+122
-5
lines changed

docs/getting-started/faq.md

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Frequently Asked Questions
2+
3+
#### Where can I find documentation and examples?
4+
While the [documentation](~/usage/resources/index.md) covers basic features and a few runnable example projects are available [here](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/src/Examples),
5+
many more advanced use cases are available as integration tests [here](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/test/JsonApiDotNetCoreTests/IntegrationTests), so be sure to check them out!
6+
7+
#### How can I debug my API project?
8+
Due to auto-generated controllers, you may find it hard to determine where to put your breakpoints. In Visual Studio, they are accessible below **Solution Explorer > Project > Dependencies > Analyzers > JsonApiDotNetCore.SourceGenerators**.
9+
10+
After turning on [Source Link](https://devblogs.microsoft.com/dotnet/improving-debug-time-productivity-with-source-link/#enabling-source-link) (which downloads the JsonApiDotNetCore source code from GitHub), you can step into its source code and add breakpoints there too.
11+
12+
Here are some key places in the execution pipeline to set a breakpoint:
13+
- `JsonApiRoutingConvention.Apply`: Controllers are registered here (executes once at startup)
14+
- `JsonApiMiddleware.InvokeAsync`: Content negotiation and `IJsonApiRequest` setup
15+
- `QueryStringReader.ReadAll`: Parses the query string parameters
16+
- `JsonApiReader.ReadAsync`: Parses the request body
17+
- `OperationsProcessor.ProcessAsync`: Entry point for handling atomic operations
18+
- `JsonApiResourceService`: Called by controllers, delegating to the repository layer
19+
- `EntityFrameworkCoreRepository.ApplyQueryLayer`: Builds the `IQueryable<>` that is offered to Entity Framework Core
20+
- `JsonApiWriter.WriteAsync`: Renders the response body
21+
- `ExceptionHandler.HandleException`: Interception point for thrown exceptions
22+
23+
Aside from debugging, you can get more info by:
24+
- Turning on exception stack traces and the incoming request body in error responses and human-readable JSON:
25+
26+
```c#
27+
// Program.cs
28+
builder.Services.AddJsonApi<AppDbContext>(options =>
29+
{
30+
options.IncludeExceptionStackTraceInErrors = true;
31+
options.IncludeRequestBodyInErrors = true;
32+
options.SerializerOptions.WriteIndented = true;
33+
});
34+
```
35+
- Turning on verbose logging and logging of executed SQL statements, by adding the following to your `appsettings.Development.json`:
36+
37+
```json
38+
{
39+
"Logging": {
40+
"LogLevel": {
41+
"Default": "Warning",
42+
"Microsoft.EntityFrameworkCore.Database.Command": "Information",
43+
"JsonApiDotNetCore": "Verbose"
44+
}
45+
}
46+
}
47+
```
48+
49+
#### What if the resources my API exposes do not exactly match the shape of my database tables?
50+
We often find users trying to write custom code to solve that. They usually get it wrong or incomplete, and it may not perform well.
51+
The good news is that there's an easier solution most of the time: configure Entity Framework Core mappings to do the work.
52+
It certainly pays off to read up on its capabilities at [Creating and Configuring a Model](https://learn.microsoft.com/en-us/ef/core/modeling/). Another great resource is [Learn Entity Framework Core](https://www.learnentityframeworkcore.com/configuration).
53+
54+
#### Can I share my resource models with .NET Framework projects?
55+
Yes, you can. Put your model classes in a separate project that only references [JsonApiDotNetCore.Annotations](https://www.nuget.org/packages/JsonApiDotNetCore.Annotations/).
56+
This package contains just the JSON:API attributes and targets NetStandard 1.0, which makes it flexible to consume. At tartup, use [Auto-discovery](~/usage/resource-graph.md#auto-discovery) and point it to your shared project.
57+
58+
#### What's the best place to put my custom business/validation logic?
59+
For basic input validation, use the attributes from [ASP.NET ModelState Validation](https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?source=recommendations&view=aspnetcore-7.0#built-in-attributes) to get the best experience.
60+
JsonApiDotNetCore is aware of them and adjusts behavior accordingly. And it produces the best possible error responses.
61+
62+
For non-trivial business rules that require custom code, the place to be are [Resource Definitions](~/usage/extensibility/resource-definitions.md). They provide a callback-based model where you can respond to everything going on.
63+
The great thing is that your callbacks are invoked for various endpoints. For example, the filter callback on Author executes at `GET /authors?filter=`, `GET /books/1/authors?filter=` and `GET /books?include=authors?filter[authors]=`.
64+
Likewise, the callbacks for changing relationships execute for POST/PATCH resource endpoints, as well as POST/PATCH/DELETE relatiionship endpoints.
65+
66+
#### How can I enable users to send multiple changes in a single request?
67+
Activate [atomic operations](~/usage/writing/bulk-batch-operations.md). It enables to send multiple changes in a batch request, which are executed in a transaction. If something fails, all changes are rolled back (the error response indicates which operation failed).
68+
69+
#### Is there any way to add `[Authorize(Roles = "...")]` to the generated controllers?
70+
This is possible if the attribute is added at the class level. See the docs on [Augmenting controllers](~/usage/extensibility/controllers.md#augmenting-controllers).
71+
72+
#### How do I expose non-JSON:API endpoints?
73+
Just add your own controllers that do not derive from (Base)JsonApiController or (Base)JsonApiOperationsController. Whatever you do in those is completely ignored by JsonApiDotNetCore.
74+
This is useful if you want to add RPC-style endpoints or binary file uploads/downloads.
75+
76+
A middle-ground approach is to add custom action methods to existing JSON:API controllers. While you can route them they way you like, they must return JSON:API resources. And on error, a JSON:API error response is produced.
77+
This is useful if you want to stay in the JSON:API-compliant world, but need to expose something on-standard, for example: `GET /users/me`.
78+
79+
#### How do I optimize for high scalability and prevent denial of service?
80+
Not so much. JsonApiDotNetCore never executes filtering, sorting or pagination in-memory and always tries to produce the most efficient query possible. There are a few things to keep in mind, though:
81+
- Prevent users from executing slow queries by locking down [attribute capabilities](~/usage/resources/attributes.md#capabilities) and [relationship capabilities](~/usage/resources/relationships.md#capabilities). Ensure the right database indexes are in place for what you enable.
82+
- Prevent users from fetching lots of data by tweaking [maximum page size/number](~/usage/options.md#pagination) and [maximum include depth](~/usage/options.md#maximum-include-depth).
83+
- Tell your users to utilize [E-Tags](~/usage/caching.md) to reduce network traffic.
84+
- Not included in JsonApiDotNetCore: Apply general practices such as rate limiting, authentication, authorization, block very large URLs/request bodies etc.
85+
86+
#### Can I offload requests to a background process?
87+
Yes, that's possible. Override controller methods to return `HTTP 202 Accepted`, with a `Location` HTTP header where users can retrieve the result.
88+
Your controller method needs to store the request state (URL, query string and request body) in a qeueue, which your background process can read from.
89+
From within your background process job handler, reconstruct the request state, execute the appropriate `JsonApiResourceService` method and store the result.
90+
There's a basic example available at https://github.com/json-api-dotnet/JsonApiDotNetCore/pull/1144, which captures a query string.
91+
92+
#### What if I don't want to use Entity Framework Core?
93+
This basically means you'll need to implement data access yourself. There are two approaches for interception: at the resource service level or at the repository level.
94+
Either way, you can use the built-in query string and request body parsing, as well as routing and rendering responses.
95+
96+
Here are some injectable request-scoped types to be aware of:
97+
- `IJsonApiRequest`: This contains routing information, such as whether a primary, secondary or relationship endpoint is being accessed.
98+
- `ITargetedFields`: Lists the attributes and relationships from an incoming POST/PATCH resource request. Any fields missing there should not be stored (partial updates).
99+
- `IEnumerable<IQueryConstraintProvider>`: Provides access to the parsed query string parameters.
100+
- `IEvaluatedIncludeCache`: This tells the response serializer which related resources to render, which you need to populate.
101+
- `ISparseFieldSetCache`: This tells the response serializer which fields to render in the attributes and relationship objects. You need to populate this as well.
102+
103+
You may also want to inject the singletons `IJsonApiOptions` (contains settings like default page size) and `IResourceGraph` (the JSON:API model of resources and relationships).
104+
105+
So, back to the topic of where to intercept. It helps to familiarize yourself with the [execution pipeline](~/internals/queries.md).
106+
Replacing at the service level is the simplest. But it means you'll need to read the parsed query string parameters and invoke
107+
all resource definition callbacks yourself. And you won't get change detection (HTTP 203 Not Modified).
108+
Take a look at [JsonApiResourceService](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/src/JsonApiDotNetCore/Services/JsonApiResourceService.cs) to see what you're missing out on.
109+
110+
You'll get a lot more out of the box if replacing at the repository level instead. There's no need to read options, analyze query strings or populate caches for the serializer. And most resource definition callbacks are handled.
111+
That's because the built-in resource service translates all JSON:API aspects of the request URL into a database-agnostic data structure called `QueryLayer`.
112+
Now the hard part becomes to read that data structure and produce data access calls from that.
113+
If your data store provides a LINQ provider, you may reuse most of [QueryableBuilder](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/QueryableBuilder.cs),
114+
which drives the translation into [System.Linq.Expressions](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/).
115+
Note however that it also produces calls to `.Include("")`, which is an Entity Framework Core-specific extension method, so you'll likely need to prevent that from happening.
116+
117+
Tip: [ExpressionTreeVisualizer](https://github.com/zspitz/ExpressionTreeVisualizer) is very helpful in trying to debug LINQ expression trees.

docs/getting-started/toc.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# [Installation](install.md)
2+
3+
# [Step By Step](step-by-step.md)
4+
5+
# [FAQ](faq.md)

docs/getting-started/toc.yml

-5
This file was deleted.

0 commit comments

Comments
 (0)