Search code examples
.netrest.net-coreargumentnullexception

Rest API : Value cannot be null. (Parameter 'text') on GET Method


In my REST API in .net core, I created a route that returns me for an article code, the description of the article. But if the article does not exist or if the Description column of the article is empty (NULL) then I want my API to return me null. Here is my route and my service:

public static RouteGroupBuilder MapArticlesEndPoint(this IEndpointRouteBuilder routes)
{
    var groupName = "Article";
    var group = routes
        .MapGroup("/article")
        .WithTags(groupName);

    group.MapGet("/{code}/description", async (
        [AsParameters] ArticlesGetDescriptionRequest request, IValidator<ArticlesGetDescriptionRequest> validator
        , IArticlesServices service, CancellationToken cancellationToken)
        => await service.GetDescriptionAsync(request, validator, cancellationToken))
        .WithName(groupName, nameof(IArticlesServices.GetDescriptionAsync))
        .WithDescription(@"Return article description");

    return group;
}
public async Task<string?> GetDescriptionAsync(ArticlesGetDescriptionRequest request, IValidator<ArticlesGetDescriptionRequest> validator, CancellationToken cancellationToken = default)
{
    await validator.ValidateAndThrowAsync(request, cancellationToken);
    var spec = new ArticlesGetDescriptionSpec(request.Code);
    return await _repository.FirstOrDefaultAsync(spec, cancellationToken);
}

When I test my route with an existing article code then the description is returned, but when I test with a non-existent article code or with an article that has no description then I have this error on Postman:

"type": "https://tools.ietf.org/html/rfc7231#section-6.6.1",
"title": "System.ArgumentNullException",
"status": 500,
"detail": "Value cannot be null. (Parameter 'text')",
"exception": {
    "details": "System.ArgumentNullException: Value cannot be null. (Parameter 'text')\r\n   at Microsoft.AspNetCore.Http.HttpResponseWritingExtensions.WriteAsync(HttpResponse response, String text, CancellationToken cancellationToken)\r\n   at Microsoft.AspNetCore.Http.RequestDelegateFactory.<ExecuteTaskOfString>g__ExecuteAwaited|113_0(Task`1 task, HttpContext httpContext)\r\n   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)\r\n   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)",

However my service does return object of type string? so the null should be accepted as a return value.

How to correct this problem ?


Solution

  • The problem is basically described in the exception:

    Value cannot be null. (Parameter 'text')\r\n   at Microsoft.AspNetCore.Http.HttpResponseWritingExtensions.WriteAsync(HttpResponse response, String text, CancellationToken cancellationToken)
    

    This WriteAsync method writes content to the response. In case that your GetDescriptionAsync returns null, the mapping tries to pass this null to the response writing middleware (text parameter) which fails.

    In general I would say that MapGet is fine for really simple tasks, like returning ping of some static content. I'd say in your case you would be better off with a normal ASP.NET controller. Your logic is simple, but not trivial anymore.

    If you want to stick to the minimal API, have a look here. You need to define both results, string and NoContent (for NULL) in your route.

    app.MapGet("/{code}/description", Results<NoContent, Ok<string>> ...