json-api-dotnet/JsonApiDotNetCore

Unable to POST when there is an ApplicationUser relationship

dubbajubba opened this issue ยท 4 comments

SUMMARY

I am trying to create a new comment by a user. I am using EF Core Identity for my users. The comment has a relationship to a user, and when I try to POST to my /comments endpoint, I am getting a validation error and 422 response: "detail": "The Id field is required." on the relationship.

DETAILS

This is what my application user Entity looks like:

[Resource(GenerateControllerEndpoints = JsonApiEndpoints.None)]
public class ApplicationUser : IdentityUser, IIdentifiable<string>
{
    [NotMapped]
    public string StringId { get => Id; set => Id = value; }

    [NotMapped]
    public string LocalId { get => Id; set => Id = value; }

    [Attr]
    public override string? UserName { get; set; }

    [Attr]
    public override bool EmailConfirmed { get; set; }

    [MaxLength(100)]
    public string? RefreshToken { get; set; }
    public DateTime RefreshTokenExpiryTime { get; set; }

    [HasMany]
    public virtual List<Comment> Comments { get; set; } = [];
}

And this is what my comment entity looks like:

[Resource(GenerateControllerEndpoints = JsonApiEndpoints.None)]
public class Comment : Identifiable<int>, IAuditableEntity
{
    [Attr]
    [Required]
    [StringLength(255)]
    public string Title { get; set; } = string.Empty;

    [Attr]
    public string? Comment { get; set; }

    [Attr]
    [EnumDataType(typeof(CommentType))]
    public CommentType Type { get; set; } = CommentType.COMMENT;

    [HasOne]
    public ApplicationUser? User { get; set; }

    [Attr]
    public DateTime DtCreated { get; set; }
    [Attr]
    public DateTime DtLastUpdated { get; set; }

    public string CreatedById { get; set; } = string.Empty;
    public string LastUpdatedById { get; set; } = string.Empty;
}

In my controller I have this POST method:

[HttpPost]
public override async Task<IActionResult> PostAsync([FromBody] Comment comment, CancellationToken cancellationToken)
{
    return await base.PostAsync(comment, cancellationToken);
}

And here is the request body:

{
    "data": {
        "type": "comments",
        "attributes": {
           "title": "New comment",
           "comment": "Here is my first comment",
        },
        "relationships": {
            "user": {
                "data": {
                    "id": "01ae21cc-9b0f-4b38-9c93-2dfc53aad7bb",
                    "type": "applicationUsers"
                }
            }
         }
     }
}

When I make a post request I get this error:

{
    "links": {
        "self": "https://localhost:7021/api/v1/comments"
    },
    "errors": [
        {
            "id": "01ae21cc-9b0f-4b38-9c93-2dfc53aad7bb",
            "status": "422",
            "title": "Input validation failed.",
            "detail": "The Id field is required.",
            "source": {
                "pointer": "/data/relationships/user/data/id"
            }
        }
    ]
}

You will notice that it says the "Id" field is missing (with a capital I). This is where I think the issue is occurring, I think jsonapi.net is having trouble matching the incoming data to the ApplicationUser. This is only happening on POSTs where there is a relationship to the ApplicationUser. I am also able to query application users from my /users endpoint with no issue.

Any help would be appreciated! I've been pulling out my hair trying to understand what is causing this. Thanks!

(Also, thanks for an amazing project! This is my first project to use jsonapi.net, but it will likely be my go-to going forward)

VERSIONS USED

  • JsonApiDotNetCore version: 5.6.0
  • ASP.NET Core version: 8.0
  • Entity Framework Core version: 8.0.7
  • Database provider: Microsoft SQL

Hi @dubbajubba, thanks for reaching out. Glad to hear you like this project!

We have a working sample that uses EF Core identity similarly. Comparing https://github.com/json-api-dotnet/TodoListExample/blob/master/TodoListAPI/Models/ApplicationUser.cs with yours, I noticed StringId and LocalId are nullable there. Also, don't map LocalId to Id, it's something else entirely.

The capitalization is not something to worry about. ASP.NET produces that message, we don't override that so built-in localization can kick in. The pointer is what's more important.

@bkoelman, thanks for the reply!

I was able to fix the error by removing the mapping of localid to id. Thanks so much for the help! You save me a lot of time.

Here is my updated ApplicationUser class for reference:

[Resource(GenerateControllerEndpoints = JsonApiEndpoints.None)]
public class ApplicationUser : IdentityUser, IIdentifiable<string>
{
    [NotMapped]
    public string? StringId { get => Id; set => Id = value; }

    [NotMapped]
    public string? LocalId { get; set; }

    [Attr]
    public override string? UserName { get; set; }

    [Attr]
    public override bool EmailConfirmed { get; set; }

    [MaxLength(100)]
    public string? RefreshToken { get; set; }
    public DateTime RefreshTokenExpiryTime { get; set; }

    [HasMany]
    public virtual List<Comment> Comments { get; set; } = [];
}

Happy to help out. Would you mind adding a star to the repo?

Done! Thanks again!