I'm trying to deliver a zip archive in response to an AJAX POST request made from Axios to WebAPI.
On the client side I have
import AjaxDownload from "../../data/AjaxDownload";
AjaxDownload.post(id, pageRecords, {
            responseType: "blob"
        }).then((response) => {
            let blob = new Blob([response.data], { type: extractContentType(response) }),
                url = window.URL.createObjectURL(blob);    
            window.open(url, "_self");
        }).catch((error) => {
            // ...
        }).then(() => {
            // ...
        });
function extractContentType(response: AxiosResponse): string {
    return response.headers["content-type"] || "";
}
// AjaxDownload:
import * as axios from "axios";
import { apiUrl } from "./Ajax";
const ajax = axios.default.create({
    baseURL: new URL("download", apiUrl).toString(),
    timeout: 3600000    // 1 hour
});
export default ajax;
That posts to the following WebAPI method - and the POST part of that client-side logic works exactly as expected.
[HttpPost]
[Route("download/{id:guid}")]
public async Task<HttpResponseMessage> Download(Guid id, [FromBody] IEnumerable<PageRecord> pageRecords)
{
    var stream = await _repo.GetAsArchiveStream(id,
                                                pageRecords,
                                                true).ConfigureAwait(false);
    stream.Position = 0;
    var result = new HttpResponseMessage(HttpStatusCode.OK) {Content = new StreamContent(stream)};
    result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {FileName = $"{...}.zip"};
    result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");    // "application/zip" has same result
    result.Content.Headers.ContentLength = stream.Length;
    return result;
}
However, the browser displays the result.Content as a JSON object, without the zip archive. I assume that the it's displaying as JSON because the request mentions JSON, but why does it appear to ignore the binary content - particularly as the Content-Type header details the type of content?
And as you can see, the JavaScript is also expecting to read in the content as a blob.
I don't see how my code differs meaningfully from this answer - please explain if there is a crucial difference.
On the server-side, I've also tried returning...
return new FileStreamResult(stream, "application/zip");
The problem with this approach is that there's no way to set a filename. Firefox does download the zip albeit with a random name while Chrome doesn't appear to download anything at all.
There must be a way to do this, right? To POST a request to a WebAPI method which returns a zip archive, and the client-side then presents a Save dialog? What am I missing?