aspnetcore icon indicating copy to clipboard operation
aspnetcore copied to clipboard

Endpoint object serialisation overlooks a public field

Open hu1buerger opened this issue 3 years ago • 8 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues

Describe the bug

After returning from the endpoint GET /issue/1 i expect the json to contain the field project with the value 1. As this is the value set in the DTO.

But it holds {}

Expected Behavior

I expect the serialisation to contain all fields with their value as described in the DTO Project.

Steps To Reproduce

  1. Given the attached project, start the project
  2. Setup an issue at the endpoint
curl -X 'POST' \
  'https://localhost:7174/issue/1' \
  -H 'accept: */*' \
  -H 'Content-Type: multipart/form-data' \
  -F 'author=a' \
  -F 'comment=b'
  1. Retrieve the issue via GET /issue/1. Now i expect that projectId is set to 1 in the first element of the list

bugreport.zip

Exceptions (if any)

No response

.NET Version

7.0.100-preview.6.22352.1

Anything else?

.NET SDK:
 Version:   7.0.100-preview.6.22352.1
 Commit:    492644e08e

Runtime Environment:
 OS Name:     Mac OS X
 OS Version:  12.5
 OS Platform: Darwin
 RID:         osx.12-x64
 Base Path:   /usr/local/share/dotnet/sdk/7.0.100-preview.6.22352.1/

Host:
  Version:      7.0.0-preview.6.22324.4
  Architecture: x64
  Commit:       d3fa592f6d

.NET SDKs installed:
  6.0.400 [/usr/local/share/dotnet/sdk]
  7.0.100-preview.6.22352.1 [/usr/local/share/dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 6.0.8 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 7.0.0-preview.6.22330.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 6.0.7 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.8 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 7.0.0-preview.6.22324.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

Other architectures found:
  None

Environment variables:
  Not set

global.json file:
  /Users/user/Documents/backend/global.json

Learn more:
  https://aka.ms/dotnet/info

Download .NET:
  https://aka.ms/dotnet/download

hu1buerger avatar Sep 13 '22 09:09 hu1buerger

As far as i could find the serialisation of responses is described here

And therefore the DataMemberAttribute should be in effect but it seems not to be. Also swagger notices the id field

hu1buerger avatar Sep 13 '22 09:09 hu1buerger

@javiercn is this just missconfiguration, or dose aspnet really drop attributes?

hu1buerger avatar Sep 13 '22 18:09 hu1buerger

@brunolins16 is there a quick fix available?

hu1buerger avatar Sep 14 '22 05:09 hu1buerger

@Hu1buerger sorry for the delay in answering your question.

As far as i could find the serialisation of responses is described here

And therefore the DataMemberAttribute should be in effect but it seems not to be. Also swagger notices the id field

The documentation you provide targets ASP.NET Web API and not ASP.NET Core. Here some docs that might cover the same:

  • https://docs.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-7.0
  • https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-customize-properties?pivots=dotnet-7-0
  • https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-ignore-properties?pivots=dotnet-7-0

brunolins16 avatar Sep 16 '22 18:09 brunolins16

Also, I looked at your application and probably you are already aware of, but the problem is MVC, to keep the polymorphic behavior, will try to get the runtime type to serialize your result. That means, in your case the runtime type is Issue not IssueDTO and the Issue.Project does not have a public property ProjectId (it does have a public field).

https://github.com/dotnet/aspnetcore/blob/7fac52dea79a90143715201d2b1f62289d1ef608/src/Mvc/Mvc.Core/src/Formatters/SystemTextJsonOutputFormatter.cs#L75

I think your goal is to use the DTO (make totally sense) however you could not cast an IEnumerable<Issue> to IEnumerable<IssueDTO> and the easier way, but not the best performance, is to create a new list. Something like this:

    [HttpGet("{projectId}")]
    public ActionResult<IEnumerable<IssueDto>> IssuesByProject(int projectId){
        if (_projectService.ProjectById(projectId).IsNone)
            return BadRequest("project dosnt exist");

        var issues = _issueService.Issues(projectId);
        return Ok(issues.Select(i => (IssueDto)i));
    }

brunolins16 avatar Sep 16 '22 18:09 brunolins16

Hi @Hu1buerger. We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

ghost avatar Sep 16 '22 18:09 ghost

This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment. If it is closed, feel free to comment when you are able to provide the additional information and we will re-investigate.

See our Issue Management Policies for more information.

ghost avatar Sep 20 '22 19:09 ghost

@brunolins16 will try that one out

hu1buerger avatar Sep 22 '22 21:09 hu1buerger

@Hu1buerger let us know in case you need any additional information, Thanks

brunolins16 avatar Sep 26 '22 18:09 brunolins16

This issue has been resolved and has not had any activity for 1 day. It will be closed for housekeeping purposes.

See our Issue Management Policies for more information.

ghost avatar Sep 27 '22 19:09 ghost

Works. @brunolins16 why isnt the implicit operation performed before the serialisation? IMHO the projection should be performed transparently. What edge case am i overlooking?

There should be an implicit operation defined on Issue with

    public static implicit operator IssueDto(Issue i) => new IssueDto(i.Project.ProjectId.ToString(), i.IssueId,
        i.Comments.Select(c => new IssueCommentDto(new Author(c.Author), c.CreatedAt, c.Comment)).ToList());

hu1buerger avatar Sep 28 '22 11:09 hu1buerger