Description
Please read our Contributing Guides before submitting a bug.
DESCRIPTION
I have an ASP.Net MVC app that has a mix of JSONAPI and non-JSONAPI apis.
Only one class extends the BaseJsonApiController
while all the others extend ControllerBase
.
For my JSONAPIs, GET requests work fine with no issues but when I try to call my PATCH API, the request body is not binding at all and instead shows as NULL, throwing me the error Value cannot be null. (Parameter 'resource')
But when I check the debug logs, the JsonApiReader acknowledges the request and even says that it has received the request.
2021-07-04 23:24:52.9458|TRACE|JsonApiDotNetCore.Serialization.JsonApiReader|Received request at 'https://localhost:44370/api/1.0/Employer/Timesheet/member-job-allocations/2c378683-86a9-4e18-9b37-01e85464717c' with body: <<{
"id": "618AC908-FFF8-4995-9D14-C9A001609DD7",
"type": "memberJobAllocations",
"attributes": {
"allocatedDateUtc": "2021-01-01",
"deAllocatedDateUtc": "2021-01-01",
"deAllocatedReason": "Absent every day",
"deAllocatedReasonCode": "New"
},
"relationships": {
"data": {
"id": "618AC908-FFF8-4995-9D14-C9A001609DD7",
"type": "members",
"attributes": {
"firstName": "Joe",
"lastName": "Bloggs",
"emailAddress": "joe@bloggs.com",
"address": "342 Wharerata Rd",
"city": "Gisborne",
"statusCode": "New",
"isRegistered": true,
"isVerified": true
}
}
}
}>>
2021-07-04 23:25:27.3640|TRACE|JsonApiDotNetCore.Controllers.BaseJsonApiController|Entering PatchAsync(id: 2c378683-86a9-4e18-9b37-01e85464717c, resource: null)
2021-07-04 23:25:27.6127|ERROR|JsonApiDotNetCore.Middleware.ExceptionHandler|Value cannot be null. (Parameter 'resource')
2021-07-04 23:25:27.7013|TRACE|JsonApiDotNetCore.Serialization.JsonApiWriter|Sending 500 response for PATCH request at 'https://localhost:44370/api/1.0/Employer/Timesheet/member-job-allocations/2c378683-86a9-4e18-9b37-01e85464717c' with body: <<{"errors":[{"id":"9878f5dd-1f9d-4c28-87cc-51a102cb8e1f","status":"500","title":"An unhandled error occurred while processing this request.","detail":"Value cannot be null. (Parameter 'resource')"}]}>>
I tried two types of request body formats (one generated by Swagger and one that follows JSON API spec) but none seem to work.
Below are the files that I have created.
Note that I have omitted non-JSONAPI code.
Models
public class MemberJobAllocation : Identifiable<Guid>
{
public override Guid Id { get; set; }
// Attributes
[Attr]
public DateTime? AllocatedDateUtc { get; set; }
[Attr]
public DateTime? DeallocatedDateUtc { get; set; }
[Attr]
public string DeallocatedReason { get; set; }
[Attr]
public string DeallocatedReasonCode { get; set; }
// Relationships
[HasOne]
public Member Member { get; set; }
}
public class Member : Identifiable<Guid>
{
public override Guid Id { get; set; }
// Attributes
[Attr]
public string FirstName { get; set; }
[Attr]
public string LastName { get; set; }
[Attr]
public string EmailAddress { get; set; }
[Attr]
public string Address { get; set; }
[Attr]
public string City { get; set; }
[Attr]
public string StatusCode { get; set; }
[Attr]
public bool? IsRegistered { get; set; }
[Attr]
public bool? IsVerified { get; set; }
}
Controllers
[DisableRoutingConvention]
[ApiVersion("1.0")]
public class MemberJobAllocationController : BaseJsonApiController<MemberJobAllocation, Guid>
{
private readonly IAuthorizationService _authorizationService;
public MemberJobAllocationController(IJsonApiOptions options, ILoggerFactory loggerFactory, IResourceService<MemberJobAllocation, Guid> resourceService, IAuthorizationService authorizationService)
: base(options, loggerFactory, resourceService)
{
_authorizationService = authorizationService;
}
// THIS IS WHERE THE ERROR OCCURS
// memberJobAllocation does not bind the request body and instead shows NULL
[HttpPatch]
[Microsoft.AspNetCore.Mvc.Route("api/{version:apiVersion}/Employer/Timesheet/member-job-allocations/{id}")]
public override async Task<IActionResult> PatchAsync(Guid id, [FromBody] MemberJobAllocation memberJobAllocation, CancellationToken cancellationToken)
{
return await base.PatchAsync(id, memberJobAllocation, cancellationToken);
}
}
DbContext
public class TimesheetDbContext : DbContext
{
public TimesheetDbContext(DbContextOptions<TimesheetDbContext> options) : base(options)
{
}
public DbSet<MemberJobAllocation> MemberJobAllocation { get; set; }
public DbSet<Member> Member { get; set; }
}
Startup.ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
// JSON API
// Add the Entity Framework Core DbContext like you normally would
services.AddDbContext<TimesheetDbContext>(options =>
{
// Use whatever provider you want, this is just an example
options.UseSqlServer(_someconnectionstringhere_);
});
// Add JsonApiDotNetCore
services.AddJsonApi<TimesheetDbContext>();
// ...non-JSONAPI code below
}
Startup.Configure
public void Configure(IApplicationBuilder app, AppDbContext context)
{
// ...non-JSONAPI code here
app.UseRouting();
// ...non-JSONAPI code here
context.Database.EnsureCreated();
app.UseJsonApi();
app.UseEndpoints(endpoints => endpoints.MapControllers());
}
PATCH Request URL and Body
https://localhost:44370/api/1.0/Employer/Timesheet/member-job-allocations/2c378683-86a9-4e18-9b37-01e85464717c
Swagger-generated format
{
"id": "2c378683-86a9-4e18-9b37-01e85464717c",
"allocatedDateUtc": "2021-07-04T09:16:52.097Z",
"deallocatedDateUtc": "2021-07-04T09:16:52.097Z",
"deallocatedReason": "string",
"deallocatedReasonCode": "string",
"member": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"firstName": "string",
"lastName": "string",
"emailAddress": "string",
"address": "string",
"city": "string",
"statusCode": "string",
"isRegistered": true,
"isVerified": true,
"stringId": "string",
"localId": "string"
},
"stringId": "string",
"localId": "string"
}
JSONAPI format
{
"id": "618AC908-FFF8-4995-9D14-C9A001609DD7",
"type": "memberJobAllocations",
"attributes": {
"allocatedDateUtc": "2021-01-01",
"deAllocatedDateUtc": "2021-01-01",
"deAllocatedReason": "Absent every day",
"deAllocatedReasonCode": "New"
},
"relationships": {
"data": {
"id": "618AC908-FFF8-4995-9D14-C9A001609DD7",
"type": "members",
"attributes": {
"firstName": "Joe",
"lastName": "Bloggs",
"emailAddress": "joe@bloggs.com",
"address": "342 Wharerata Rd",
"city": "Gisborne",
"statusCode": "New",
"isRegistered": true,
"isVerified": true
}
}
}
}
EXPECTED BEHAVIOR
[FromBody] MemberJobAllocation memberJobAllocation
binds values coming from the request body
ACTUAL BEHAVIOR
[FromBody] MemberJobAllocation memberJobAllocation
is NULL
{
"errors": [
{
"id": "6be4f128-3ec0-402c-b4c6-573d69ad1842",
"status": "500",
"title": "An unhandled error occurred while processing this request.",
"detail": "Value cannot be null. (Parameter 'resource')"
}
]
}
VERSIONS USED
- JsonApiDotNetCore version: 4.2.0
- ASP.NET Core version: 3.1
- Entity Framework Core version: 5.0.7
- Database provider: SQL Server