question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

`System.InvalidOperationException` when getting a `404 NOT FOUND` response from an endpoint

See original GitHub issue

I’m just starting using this library to create set of microservices using the vertical slice feature approach. The endpoint under test


/// <summary>
/// Gets an appointment by its id
/// </summary>
public class Endpoint : Endpoint<AppointmentId, Browsable<AppointmentInfo>>
{
    private readonly IUnitOfWorkFactory _unitOfWorkFactory;
    private readonly LinkGenerator _linkGenerator;


    /// <inheritdoc/>
    public override void Configure()
    {
        Get("/appointments/{@id}", x => new { Id = x.Value });
        Head("/appointments/{@id}", x => new { Id = x.Value });
        AllowAnonymous();
    }

    /// <inheritdoc/>
    public override async Task HandleAsync(AppointmentId req, CancellationToken ct)
    {
        using IUnitOfWork unitOfWork = _unitOfWorkFactory.NewUnitOfWork();
        Option<AppointmentInfo> mayBeAppointment = await unitOfWork.Repository<Appointment>()
                                                                   .SingleOrDefault(selector: appointment => new AppointmentInfo
                                                                   {
                                                                       Id = appointment.Id,
                                                                       Subject = appointment.Subject,
                                                                       Location = appointment.Location,
                                                                       EndDate = appointment.EndDate,
                                                                       StartDate = appointment.StartDate,
                                                                       Attendees = appointment.Attendees.Select(attendee => new AttendeeInfo
                                                                       {
                                                                           Id = attendee.Id,
                                                                           Email = attendee.Email,
                                                                           Name = attendee.Name,
                                                                           PhoneNumber = attendee.PhoneNumber,
                                                                       }),
                                                                   },
                                                                                    predicate: (Appointment x) => x.Id == req,
                                                                                    cancellationToken: ct).ConfigureAwait(false);

        await mayBeAppointment.Match(
            some: async appointment =>
            {
                Browsable<AppointmentInfo> browsable = new()
                {
                    Resource = appointment,
                    Links = new[]
                    {
                        new Link
                        {
                            Href = _linkGenerator.GetUriByName(HttpContext, nameof(Endpoint), new { Id = req }),
                            Method = "GET",
                            Relations = new [] { LinkRelation.Self }.ToHashSet()
                        },
                    }
                };
                await SendOkAsync(browsable, ct).ConfigureAwait(false);
            },
            none: async () => await SendNotFoundAsync(ct).ConfigureAwait(false))
                              .ConfigureAwait(false);
    }
}

The test basically checks that I indeed get a 404 when I hit the endpoint with an unknown id

    public class EndpointShould : IClassFixture<AgendaWebApplicationFactory>
    {
        private readonly HttpClient _client;
        private readonly ITestOutputHelper _outputHelper;
        private readonly AgendaWebApplicationFactory _applicationFactory;

        [Fact]
        public async Task Returns_NotFound_when_Id_does_not_exist()
        {
            // Arrange
            AppointmentId appointmentId = AppointmentId.New();

            // Act
            (HttpResponseMessage response, AppointmentInfo _) = await _client.GETAsync<Endpoint, AppointmentId, AppointmentInfo>(appointmentId);

            // Assert
            response.StatusCode.Should().Be(System.Net.HttpStatusCode.NotFound);
        }
    }

I get the following error as soon as I run the test

 Message: 
System.InvalidOperationException : Unable to deserialize the response body as [Agenda.API.Resources.v1.Appointments.AppointmentInfo]. Reason: [NotFound] 

  Arborescence des appels de procédure: 
HttpClientExtensions.Send[TRequest,TResponse](HttpClient client, HttpMethod method, String requestUri, TRequest request, Nullable`1 isFormContent)

This seems to be due to the HttpClientExtensions.Send method which always deserializes the json for endpoints that are supposed to returns a response, no matter what the HTTP status code is.

A fix could be something like the following

        if (typeof(TResponse) == Types.EmptyResponse)
            return new(rsp, default);

        TResponse? res;
       
       if(CanDeserializeContent(rsp)) // <-- This method would check that there is a content to deserialize based on status code, content directly etc....
      {
        try
        {
            res = await rsp.Content.ReadFromJsonAsync<TResponse>(SerOpts.Options);
        }
        catch (JsonException)
        {
            var reason = $"[{rsp.StatusCode}] {await rsp.Content.ReadAsStringAsync()}";
            throw new InvalidOperationException(
                $"Unable to deserialize the response body as [{typeof(TResponse).FullName}]. Reason: {reason}");
        }
   }
        return new(rsp, res);
    }


   static bool CanDeserialize(HttpResponseMessage response) => response.StatusCode != 404

I can make a PR if it’s okay for you

Issue Analytics

  • State:closed
  • Created 5 months ago
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
dj-nitehawkcommented, Apr 19, 2023

final release will be v6.0 for sure.

1reaction
dj-nitehawkcommented, Apr 19, 2023

@candoumbe

decided to do a breaking change and finally sort this out: https://github.com/FastEndpoints/FastEndpoints/blob/main/Src/Library/changelog.md#breaking-changes

available in v5.8.1.10-beta

Read more comments on GitHub >

github_iconTop Results From Across the Web

ASP.NET WebApi returns HTTP 404 instead of HTTP 401
From your description, I think it is caused by when you accessing an endpoint protected by [Authroize] , If you are not logged...
Read more >
404 Not Found When accessing controller action ...
You're seeing a 404 response because the cookies handler managed by Identity is trying to redirect you to the login page, for which...
Read more >
Error 404: 4 Ways to Fix It
Error 404 is a response code, meaning the server could not locate the requested content. Check this article to learn 4 steps to...
Read more >
404 Not Found Error: What It Is and How to Fix It
The 404 Not Found Error is an HTTP response status code, which indicates that the requested resource could not be found. Like most...
Read more >
why is 404 happened when i request action in a web API ...
when i set [Authorize] to controller , the request "/login/login",the response is "404 not found". when i delete [Authorize], ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found