diff --git a/couscous.yml b/couscous.yml index 87b795a1c3..2acaad12d7 100644 --- a/couscous.yml +++ b/couscous.yml @@ -110,4 +110,7 @@ menu: relativeUrl: entityrepositories.html middleware: text: Middleware - relativeUrl: middleware.html \ No newline at end of file + relativeUrl: middleware.html + customqueryformat: + text: Custom Query Formats + relativeUrl: customqueryformat.html \ No newline at end of file diff --git a/docs/CustomQueryFormat.md b/docs/CustomQueryFormat.md new file mode 100644 index 0000000000..b23993d333 --- /dev/null +++ b/docs/CustomQueryFormat.md @@ -0,0 +1,13 @@ +--- +currentMenu: customqueryformat +--- + +# Custom Query Formats + +For information on the default query parameter formats, see the documentation for each query method. + +In order to customize the query formats, you need to implement the `IQueryParser` interface and inject it like so: + +```csharp +services.AddScoped(); +``` \ No newline at end of file diff --git a/docs/Options.md b/docs/Options.md index 681c502557..3f94f4031e 100644 --- a/docs/Options.md +++ b/docs/Options.md @@ -80,4 +80,32 @@ Accept: application/vnd.api+json } } } +``` + +## Custom Query Parameters + +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. + +```csharp +public IServiceProvider ConfigureServices(IServiceCollection services) { + services.AddJsonApi( + opt => opt.AllowCustomQueryParameters = true); + // ... +} +``` + +## Custom Serializer Settings + +We use Json.Net for all serialization needs. If you want to change the default serializer settings, you can: + +```csharp +public IServiceProvider ConfigureServices(IServiceCollection services) { + services.AddJsonApi( + opt => opt.SerializerSettings = new JsonSerializerSettings() + { + NullValueHandling = NullValueHandling.Ignore, + ContractResolver = new DasherizedResolver() + }); + // ... +} ``` \ No newline at end of file diff --git a/docs/QueryingData.md b/docs/QueryingData.md deleted file mode 100644 index d6de50111a..0000000000 --- a/docs/QueryingData.md +++ /dev/null @@ -1,185 +0,0 @@ -# Querying Data -### Pagination - -Resources can be paginated. -The following query would set the page size to 10 and get page 2. - -``` -?page[size]=10&page[number]=2 -``` - -If you would like pagination implemented by default, you can specify the page size -when setting up the services: - -```csharp - services.AddJsonApi( - opt => opt.DefaultPageSize = 10); -``` - -**Total Record Count** - -The total number of records can be added to the document meta by setting it in the options: - -```csharp -services.AddJsonApi(opt => -{ - opt.DefaultPageSize = 5; - opt.IncludeTotalRecordCount = true; -}); -``` - -### Filtering - -You can filter resources by attributes using the `filter` query parameter. -By default, all attributes are filterable. -The filtering strategy we have selected, uses the following form: - -``` -?filter[attribute]=value -``` - -For operations other than equality, the query can be prefixed with an operation -identifier): - -``` -?filter[attribute]=eq:value -?filter[attribute]=lt:value -?filter[attribute]=gt:value -?filter[attribute]=le:value -?filter[attribute]=ge:value -?filter[attribute]=like:value -``` - -#### Custom Filters - -You can customize the filter implementation by overriding the method in the `DefaultEntityRepository` like so: - -```csharp -public class MyEntityRepository : DefaultEntityRepository -{ - public MyEntityRepository( - AppDbContext context, - ILoggerFactory loggerFactory, - IJsonApiContext jsonApiContext) - : base(context, loggerFactory, jsonApiContext) - { } - - public override IQueryable Filter(IQueryable entities, FilterQuery filterQuery) - { - // use the base filtering method - entities = base.Filter(entities, filterQuery); - - // implement custom method - return ApplyMyCustomFilter(entities, filterQuery); - } -} -``` - -### Sorting - -Resources can be sorted by an attribute: - -``` -?sort=attribute // ascending -?sort=-attribute // descending -``` - -### Meta - -Meta objects can be assigned in two ways: - - Resource meta - - Request Meta - -Resource meta can be defined by implementing `IHasMeta` on the model class: - -```csharp -public class Person : Identifiable, IHasMeta -{ - // ... - - public Dictionary GetMeta(IJsonApiContext context) - { - return new Dictionary { - { "copyright", "Copyright 2015 Example Corp." }, - { "authors", new string[] { "Jared Nance" } } - }; - } -} -``` - -Request Meta can be added by injecting a service that implements `IRequestMeta`. -In the event of a key collision, the Request Meta will take precendence. - -### Client Generated Ids - -By default, the server will respond with a `403 Forbidden` HTTP Status Code if a `POST` request is -received with a client generated id. However, this can be allowed by setting the `AllowClientGeneratedIds` -flag in the options: - -```csharp -services.AddJsonApi(opt => -{ - opt.AllowClientGeneratedIds = true; - // .. -}); -``` - -### Custom Errors - -By default, errors will only contain the properties defined by the internal [Error](https://github.com/Research-Institute/json-api-dotnet-core/blob/master/src/JsonApiDotNetCore/Internal/Error.cs) class. However, you can create your own by inheriting from `Error` and either throwing it in a `JsonApiException` or returning the error from your controller. - -```csharp -// custom error definition -public class CustomError : Error { - public CustomError(string status, string title, string detail, string myProp) - : base(status, title, detail) - { - MyCustomProperty = myProp; - } - public string MyCustomProperty { get; set; } -} - -// throwing a custom error -public void MyMethod() { - var error = new CustomError("507", "title", "detail", "custom"); - throw new JsonApiException(error); -} - -// returning from controller -[HttpPost] -public override async Task PostAsync([FromBody] MyEntity entity) -{ - if(_db.IsFull) - return new ObjectResult(new CustomError("507", "Database is full.", "Theres no more room.", "Sorry.")); - - // ... -} -``` - -### Sparse Fieldsets - -We currently support top-level field selection. -What this means is you can restrict which fields are returned by a query using the `fields` query parameter, but this does not yet apply to included relationships. - -- Currently valid: -```http -GET /articles?fields[articles]=title,body HTTP/1.1 -Accept: application/vnd.api+json -``` - -- Not yet supported: -```http -GET /articles?include=author&fields[articles]=title,body&fields[people]=name HTTP/1.1 -Accept: application/vnd.api+json -``` - -## Tests - -I am using DotNetCoreDocs to generate sample requests and documentation. - -1. To run the tests, start a postgres server and verify the connection properties define in `/test/JsonApiDotNetCoreExampleTests/appsettings.json` -2. `cd ./test/JsonApiDotNetCoreExampleTests` -3. `dotnet test` -4. `cd ./src/JsonApiDotNetCoreExample` -5. `dotnet run` -6. `open http://localhost:5000/docs` diff --git a/src/JsonApiDotNetCore/Controllers/HttpMethodRestrictionFilter.cs b/src/JsonApiDotNetCore/Controllers/HttpMethodRestrictionFilter.cs index 9bf533502a..ca9a2ff138 100644 --- a/src/JsonApiDotNetCore/Controllers/HttpMethodRestrictionFilter.cs +++ b/src/JsonApiDotNetCore/Controllers/HttpMethodRestrictionFilter.cs @@ -1,8 +1,5 @@ -using System; using System.Linq; -using System.Reflection; using System.Threading.Tasks; -using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Internal; using Microsoft.AspNetCore.Mvc.Filters; @@ -17,15 +14,15 @@ public override async Task OnActionExecutionAsync( ActionExecutionDelegate next) { var method = context.HttpContext.Request.Method; - - if(CanExecuteAction(method) == false) + + if (CanExecuteAction(method) == false) throw new JsonApiException(405, $"This resource does not support {method} requests."); await next(); } private bool CanExecuteAction(string requestMethod) - { + { return Methods.Contains(requestMethod) == false; } }