0

I want to download a file via Results.File(). It works when executing it in the main method (app.MapGet), however returning it in a different method does do nothing. The line where it should send the file is executed in the Debugger, however it does not return, but jumps back to the main method to execute the last return (The one that never should be executed). This last return does indeed return a result.

I have also used several methods like Result.BadRequest(), however nothing is executed when it is not in the main method.

I saw people using IResult as return method but I am not sure whether this is even right.

My guess is maybe the wrong return type or executing a Task or so.

Whole methods:

    app.MapGet("/", (HttpContext context) =>
    {
        if (context.Request.Query.ContainsKey("amount"))
        {
            if (context.Request.Query["amount"].Count > 1)
            {
                return Results.BadRequest(new { Error = "The query parameter 'amount' may only be used once." });
            }
    
            if (int.TryParse(context.Request.Query["amount"], out int amount))
            {
                if (amount <= 0)
                {
                    return Results.BadRequest(new { Error = "The specified amount must be greater or equal to 1." });
                }
    
                var list = new List<string>();
    
                for (int i = 0; i < amount; i++)
                {
                    list.Add(Ulid.NewUlid().ToString());
                }
    
                CheckIfDownload(context, (list, null));
    
                return Results.Json(new { Ulids = list });
            }
            else
            {
                return Results.BadRequest(new { Error = "The specified amount is not a valid number." });
            }
        }
    
        string ulid = Ulid.NewUlid().ToString();
    
        CheckIfDownload(context, (null, ulid));
    
        return Results.Json(new { Ulid = ulid });
    });
    
    static IResult? CheckIfDownload(HttpContext context, (List<string>? list, string? single) ulidListOrSingle)
    {
        if (context.Request.Query.ContainsKey("download"))
        {
            if (context.Request.Query["download"].Count > 1)
            {
                return Results.BadRequest(new { Error = "The query parameter 'download' may only be used once." });
            }
    
            if (context.Request.Query["download"] == "" || (bool.TryParse(context.Request.Query["download"], out bool download) && download))
            {
                if (ulidListOrSingle.list != null)
                {
                    return SendJsonFile(JsonSerializer.Serialize(ulidListOrSingle.list));
                }
    
                if (ulidListOrSingle.single != null)
                {
                    return SendJsonFile(JsonSerializer.Serialize(ulidListOrSingle.single));
                }
    
                return Results.BadRequest(new { Error = "An unknown error occurred." });
            }
        }
    
        return null;
    }

static IResult SendJsonFile(string data)
{
    byte[] buffer = Encoding.UTF8.GetBytes(data);
    var stream = new MemoryStream(buffer);

    return Results.File(stream, "application/json", $"UlidGenerator_{DateTime.Now:MM-dd-yyyy_HH-mm-ss}.json");
}
Steven2105
  • 540
  • 5
  • 20
  • 1
    `return SendJsonFile(data);` – D Stanley Jan 19 '22 at 14:44
  • And you can remove `return Results.BadRequest("This should never be executed!");` since there's no possible way that code could be reached, once you implement D Stanley's suggestion. – mason Jan 19 '22 at 14:45
  • No, that does not work, because this was only an example. I use a different IResult method that checks whether a certain query parameter is set, only then this SendJsonFile() method is executed. – Steven2105 Jan 19 '22 at 14:46
  • 2
    okay so post the actual entire method - of you only post examples ant then tell us it doesn't work because your actaul code is different, it wasted time for both of us. Or read and understand the principle behind the comment and apply it to your actual situation... – D Stanley Jan 19 '22 at 14:48
  • @DStanley I have now edited my question to include all methods (You can then ignore the example) – Steven2105 Jan 19 '22 at 14:51
  • DStanley's answer is still correct - you need to return the result of the `CheckIfDownload` method, if it is not `null`, from your `MapGet` delegate. – Richard Deeming Jan 19 '22 at 14:54
  • @RichardDeeming If I return `CheckIfDownload`, the last return of the MapGet method will never be reached, because it's returning the `CheckIfDownload` method. Also when starting, an exception occurrs saying `InvalidOperationException: The IResult returned by the Delegate must not be null.` – Steven2105 Jan 19 '22 at 15:02
  • The logic path should be `MapGet` -> execute `CheckIfDownload` method -> method checks for errors and should return badrequest if there are any -> if everything previous is valid, the `SendJsonFile` method should be executed -> this method should send the file -> after it has sent the file, it should go back to the `MapGet`, to send its last line which in this case is the `return Results.Json()` method – Steven2105 Jan 19 '22 at 15:08
  • @Steven2105 You cannot return both a file download and a JSON blob in the same response. That's not how HTTP works! – Richard Deeming Jan 19 '22 at 15:09
  • @RichardDeeming I see, how can I achieve this then, when using the minimal apis? But if we ignore the file sending for one moment, the badrequest results are also not returned (`CheckIfDownload`) – Steven2105 Jan 19 '22 at 15:12
  • @Steven2105 Because you're throwing away the value returned by your `CheckIfDownload` function. The only thing that gets sent back is what you `return` from your `MapGet` delegate. – Richard Deeming Jan 19 '22 at 15:16
  • @RichardDeeming I used a second method to avoid DRY, does that mean I have to put everything in the `MapGet` method? Or is there a better version where I don't have to repeat myself? – Steven2105 Jan 19 '22 at 15:20
  • 1
    @Steven2105 No, it means you need to return the correct value from your `MapGet` method. What you have at the moment is equivalent to me asking you for a beer, you going to the bar and purchasing a beer, then you throwing that beer away and handing me a glass of water. If you did that, would you be confused as to why I hadn't received my beer? – Richard Deeming Jan 19 '22 at 15:25
  • And what is the correct value? – Steven2105 Jan 19 '22 at 15:26
  • That's for you to determine. Do you want to return a file? Or do you want to return some other result? You can only choose one. – mason Jan 19 '22 at 20:00
  • I finally got it working. My solution was returning all `Results` and only `null` when there should be no response. The main `MapGet` method then checks whether the return value of the executed methods are null, in case it is null, it'll continue, however if it returns something else the value from the other method will be returned. – Steven2105 Jan 20 '22 at 07:46

1 Answers1

2

when you inline an entire method block (not just a single expression), you must return what you want to be the output of the block. In your case you are not capturing the result of SendJsonFile to return it:

app.MapGet("/download", () =>
{
    string data = "json data";

    var result = SendJsonFile(data);

    return result;
});
D Stanley
  • 149,601
  • 11
  • 178
  • 240