Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions aspnetcore/blazor/call-web-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ This article describes how to call a web API from a Blazor app.
> This article has loaded **Blazor WebAssembly** coverage for calling web APIs. The [Blazor Server coverage](?pivots=server) addresses the following subjects:
>
> * Use of the `HttpClient` factory infrastructure to provide an `HttpClient` to the app.
> * Cross-origin resource sharing (CORS) pertaining to Blazor Server apps.
> * Cross-Origin Resource Sharing (CORS) pertaining to Blazor Server apps.
> * Blazor framework component examples for testing web API access.
> * Additional resources for developing Blazor Server apps that call a web API.

Expand All @@ -49,7 +49,7 @@ public class TodoItem
}
```

For guidance on how to create a server-side web API, see <xref:tutorials/first-web-api>. For information on Cross-origin resource sharing (CORS), see the *Cross-origin resource sharing (CORS)* section later in this article.
For guidance on how to create a server-side web API, see <xref:tutorials/first-web-api>. For information on Cross-Origin Resource Sharing (CORS), see the *Cross-Origin Resource Sharing (CORS)* section later in this article.

The Blazor WebAssembly examples that demonstrate obtaining weather data from a server API are based on a hosted Blazor WebAssembly solution created from the [Blazor WebAssembly project template](xref:blazor/project-structure#blazor-webassembly).

Expand All @@ -75,7 +75,7 @@ builder.Services.AddScoped(sp =>

<xref:System.Net.Http.HttpClient> is available as a preconfigured service for making requests back to the origin server.

<xref:System.Net.Http.HttpClient> and JSON helpers (<xref:System.Net.Http.Json.HttpClientJsonExtensions?displayProperty=nameWithType>) are also used to call third-party web API endpoints. <xref:System.Net.Http.HttpClient> is implemented using the browser's [Fetch API](https://developer.mozilla.org/docs/Web/API/Fetch_API) and is subject to its limitations, including enforcement of the same-origin policy, which is discussed later in this article in the *Cross-origin resource sharing (CORS)* section.
<xref:System.Net.Http.HttpClient> and JSON helpers (<xref:System.Net.Http.Json.HttpClientJsonExtensions?displayProperty=nameWithType>) are also used to call third-party web API endpoints. <xref:System.Net.Http.HttpClient> is implemented using the browser's [Fetch API](https://developer.mozilla.org/docs/Web/API/Fetch_API) and is subject to its limitations, including enforcement of the same-origin policy, which is discussed later in this article in the *Cross-Origin Resource Sharing (CORS)* section.

The client's base address is set to the originating server's address. Inject an <xref:System.Net.Http.HttpClient> instance into a component using the [`@inject`](xref:mvc/views/razor#inject) directive:

Expand Down Expand Up @@ -635,7 +635,7 @@ For more information on Fetch API options, see [MDN web docs: WindowOrWorkerGlob

The following example calls a web API. The example requires a running web API based on the sample app described by the <xref:tutorials/first-web-api> article. This example makes requests to the web API at `https://localhost:10000/api/TodoItems`. If a different web API address is used, update the `ServiceEndpoint` constant value in the component's `@code` block.

The following example makes a [cross-origin resource sharing (CORS)](xref:security/cors) request from `http://localhost:5000` or `https://localhost:5001` to the web API. Add the following CORS Middleware configuration to the web API's service's `Program.cs` file:
The following example makes a [Cross-Origin Resource Sharing (CORS)](xref:security/cors) request from `http://localhost:5000` or `https://localhost:5001` to the web API. Add the following CORS Middleware configuration to the web API's service's `Program.cs` file:

```csharp
app.UseCors(policy =>
Expand Down Expand Up @@ -737,7 +737,7 @@ For more information, see <xref:blazor/fundamentals/handle-errors>.
> * `IHttpClientFactory` services and the configuration of a named `HttpClient`.
> * Typed `HttpClient`.
> * `HttpClient` and `HttpRequestMessage` to customize requests.
> * Call web API example with cross-origin resource sharing (CORS) and how CORS pertains to Blazor WebAssembly apps.
> * Call web API example with Cross-Origin Resource Sharing (CORS) and how CORS pertains to Blazor WebAssembly apps.
> * How to handle web API response errors in developer code.
> * Blazor framework component examples for testing web API access.
> * Additional resources for developing Blazor WebAssembly apps that call a web API.
Expand Down Expand Up @@ -822,9 +822,9 @@ For an additional working example, see the server-side file upload example that

:::zone-end

## Cross-origin resource sharing (CORS)
## Cross-Origin Resource Sharing (CORS)

Browser security restricts a webpage from making requests to a different domain than the one that served the webpage. This restriction is called the *same-origin policy*. The same-origin policy restricts (but doesn't prevent) a malicious site from reading sensitive data from another site. To make requests from the browser to an endpoint with a different origin, the *endpoint* must enable [cross-origin resource sharing (CORS)](https://www.w3.org/TR/cors/).
Browser security restricts a webpage from making requests to a different domain than the one that served the webpage. This restriction is called the *same-origin policy*. The same-origin policy restricts (but doesn't prevent) a malicious site from reading sensitive data from another site. To make requests from the browser to an endpoint with a different origin, the *endpoint* must enable [Cross-Origin Resource Sharing (CORS)](https://www.w3.org/TR/cors/).

:::zone pivot="webassembly"

Expand Down Expand Up @@ -879,7 +879,7 @@ Various network tools are publicly available for testing web API backend apps di

* <xref:blazor/security/webassembly/additional-scenarios>: Includes coverage on using <xref:System.Net.Http.HttpClient> to make secure web API requests.
* <xref:security/cors>: Although the content applies to ASP.NET Core apps, not Blazor WebAssembly apps, the article covers general CORS concepts.
* [Cross-Origin resource sharing (CORS) at W3C](https://www.w3.org/TR/cors/)
* [Cross-Origin Resource Sharing (CORS) at W3C](https://www.w3.org/TR/cors/)
* [Fetch API](https://developer.mozilla.org/docs/Web/API/fetch)

:::zone-end
Expand All @@ -891,6 +891,6 @@ Various network tools are publicly available for testing web API backend apps di
* <xref:security/enforcing-ssl>
* <xref:security/cors>
* [Kestrel HTTPS endpoint configuration](xref:fundamentals/servers/kestrel/endpoints)
* [Cross-Origin resource sharing (CORS) at W3C](https://www.w3.org/TR/cors/)
* [Cross-Origin Resource Sharing (CORS) at W3C](https://www.w3.org/TR/cors/)

:::zone-end
122 changes: 96 additions & 26 deletions aspnetcore/blazor/file-downloads.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: ASP.NET Core Blazor file downloads
author: guardrex
description: Learn how to download files using Blazor Server and Blazor WebAssembly.
description: Learn how to download files in Blazor apps.
monikerRange: '>= aspnetcore-6.0'
ms.author: riande
ms.custom: mvc
Expand All @@ -12,11 +12,13 @@ uid: blazor/file-downloads

[!INCLUDE[](~/includes/not-latest-version.md)]

This article explains how to download files in Blazor Server and Blazor WebAssembly apps.
This article explains how to download files in Blazor apps.

[!INCLUDE[](~/blazor/includes/location-client-and-server-net-6-or-later.md)]

Files can be downloaded from the app's own static assets or from any other location:

* ASP.NET Core apps use [Static File Middleware](xref:fundamentals/static-files) to serve files to clients of Blazor Server and hosted Blazor WebAssembly apps.
* ASP.NET Core apps use [Static File Middleware](xref:fundamentals/static-files) to serve files to clients of server-side apps.
* The guidance in this article also applies to other types of file servers that don't use .NET, such as Content Delivery Networks (CDNs).

This article covers approaches for the following scenarios:
Expand Down Expand Up @@ -45,16 +47,16 @@ The recommended approach for downloading relatively small files (\< 250 MB) is t
> [!WARNING]
> The approach in this section reads the file's content into a [JS `ArrayBuffer`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer). This approach loads the entire file into the client's memory, which can impair performance. To download relatively large files (\>= 250 MB), we recommend following the guidance in the [Download from a URL](#download-from-a-url) section.

The following `downloadFileFromStream` JS function performs the following steps:
The following `downloadFileFromStream` JS function:

* Read the provided stream into an [`ArrayBuffer`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer).
* Create a [`Blob`](https://developer.mozilla.org/docs/Web/API/Blob) to wrap the `ArrayBuffer`.
* Create an object URL to serve as the file's download address.
* Create an [`HTMLAnchorElement`](https://developer.mozilla.org/docs/Web/API/HTMLAnchorElement) (`<a>` element).
* Assign the file's name (`fileName`) and URL (`url`) for the download.
* Trigger the download by firing a [`click` event](https://developer.mozilla.org/docs/Web/API/HTMLElement/click) on the anchor element.
* Remove the anchor element.
* Revoke the object URL (`url`) by calling [`URL.revokeObjectURL`](https://developer.mozilla.org/docs/Web/API/URL/revokeObjectURL). **This is an important step to ensure memory isn't leaked on the client.**
* Reads the provided stream into an [`ArrayBuffer`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer).
* Creates a [`Blob`](https://developer.mozilla.org/docs/Web/API/Blob) to wrap the `ArrayBuffer`.
* Creates an object URL to serve as the file's download address.
* Creates an [`HTMLAnchorElement`](https://developer.mozilla.org/docs/Web/API/HTMLAnchorElement) (`<a>` element).
* Assigns the file's name (`fileName`) and URL (`url`) for the download.
* Triggers the download by firing a [`click` event](https://developer.mozilla.org/docs/Web/API/HTMLElement/click) on the anchor element.
* Removes the anchor element.
* Revokes the object URL (`url`) by calling [`URL.revokeObjectURL`](https://developer.mozilla.org/docs/Web/API/URL/revokeObjectURL). **This is an important step to ensure memory isn't leaked on the client.**

```html
<script>
Expand All @@ -75,17 +77,55 @@ The following `downloadFileFromStream` JS function performs the following steps:
> [!NOTE]
> For general guidance on JS location and our recommendations for production apps, see <xref:blazor/js-interop/index#javascript-location>.

The following example component:
The following component:

* Uses native byte-streaming interop to ensure efficient transfer of the file to the client.
* Has a method named `GetFileStream` to retrieve a <xref:System.IO.Stream> for the file that's downloaded to clients. Alternative approaches include retrieving a file from storage or generating a file dynamically in C# code. For this demonstration, the app creates a 50 KB file of random data from a new byte array (`new byte[]`). The bytes are wrapped with a <xref:System.IO.MemoryStream> to serve as the example's dynamically-generated binary file.
* The `DownloadFileFromStream` method performs the following steps:
* Retrieve the <xref:System.IO.Stream> from `GetFileStream`.
* Specify a file name when file is saved on the user's machine. The following example names the file `quote.txt`.
* Wrap the <xref:System.IO.Stream> in a <xref:Microsoft.JSInterop.DotNetStreamReference>, which allows streaming the file data to the client.
* Invoke the `downloadFileFromStream` JS function to accept the data on the client.
* The `DownloadFileFromStream` method:
* Retrieves the <xref:System.IO.Stream> from `GetFileStream`.
* Specifies a file name when file is saved on the user's machine. The following example names the file `quote.txt`.
* Wraps the <xref:System.IO.Stream> in a <xref:Microsoft.JSInterop.DotNetStreamReference>, which allows streaming the file data to the client.
* Invokes the `downloadFileFromStream` JS function to accept the data on the client.

:::moniker range=">= aspnetcore-7.0"
`FileDownload1.razor`:

:::moniker range=">= aspnetcore-8.0"

```razor
@page "/file-download-1"
@attribute [RenderModeServer]
@using System.IO
@inject IJSRuntime JS

<h1>File Download Example 1</h1>

<button @onclick="DownloadFileFromStream">
Download File From Stream
</button>

@code {
private Stream GetFileStream()
{
var randomBinaryData = new byte[50 * 1024];
var fileStream = new MemoryStream(randomBinaryData);

return fileStream;
}

private async Task DownloadFileFromStream()
{
var fileStream = GetFileStream();
var fileName = "log.bin";
using var streamRef = new DotNetStreamReference(stream: fileStream);

await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef);
}
}
```

:::moniker-end

:::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0"

:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/file-downloads/FileDownload1.razor":::

Expand All @@ -97,7 +137,7 @@ The following example component:

:::moniker-end

For a component in a Blazor Server app that must return a <xref:System.IO.Stream> for a physical file, the component can call <xref:System.IO.File.OpenRead%2A?displayProperty=nameWithType>, as the following example demonstrates:
For a component in a server-side app that must return a <xref:System.IO.Stream> for a physical file, the component can call <xref:System.IO.File.OpenRead%2A?displayProperty=nameWithType>, as the following example demonstrates:

```csharp
private Stream GetFileStream()
Expand Down Expand Up @@ -131,12 +171,12 @@ The example in this section uses a download file named `quote.txt`, which is pla

:::moniker-end

The following `triggerFileDownload` JS function performs the following steps:
The following `triggerFileDownload` JS function:

* Create an [`HTMLAnchorElement`](https://developer.mozilla.org/docs/Web/API/HTMLAnchorElement) (`<a>` element).
* Assign the file's name (`fileName`) and URL (`url`) for the download.
* Trigger the download by firing a [`click` event](https://developer.mozilla.org/docs/Web/API/HTMLElement/click) on the anchor element.
* Remove the anchor element.
* Creates an [`HTMLAnchorElement`](https://developer.mozilla.org/docs/Web/API/HTMLAnchorElement) (`<a>` element).
* Assigns the file's name (`fileName`) and URL (`url`) for the download.
* Triggers the download by firing a [`click` event](https://developer.mozilla.org/docs/Web/API/HTMLElement/click) on the anchor element.
* Removes the anchor element.

```html
<script>
Expand All @@ -155,7 +195,37 @@ The following `triggerFileDownload` JS function performs the following steps:

The following example component downloads the file from the same origin that the app uses. If the file download is attempted from a different origin, configure Cross-Origin Resource Sharing (CORS). For more information, see the [Cross-Origin Resource Sharing (CORS)](#cross-origin-resource-sharing-cors) section.

:::moniker range=">= aspnetcore-7.0"
Change the port in the following example to match the localhost development port of your environment.

`FileDownload2.razor`:

:::moniker range=">= aspnetcore-8.0"

```razor
@page "/file-download-2"
@attribute [RenderModeServer]
@inject IJSRuntime JS

<h1>File Download Example 2</h1>

<button @onclick="DownloadFileFromURL">
Download File From URL
</button>

@code {
private async Task DownloadFileFromURL()
{
var fileName = "quote.txt";
var fileURL = Path.Combine("https://localhost:7029", "files", fileName);

await JS.InvokeVoidAsync("triggerFileDownload", fileName, fileURL);
}
}
```

:::moniker-end

:::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0"

:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/file-downloads/FileDownload2.razor":::

Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/file-uploads.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ uid: blazor/file-uploads

This article explains how to upload files in Blazor with the <xref:Microsoft.AspNetCore.Components.Forms.InputFile> component.

[!INCLUDE[](~/blazor/includes/location-client-and-server.md)]
[!INCLUDE[](~/blazor/includes/location-client-and-server-net31-or-later.md)]

> [!WARNING]
> Always follow security best practices when permitting users to upload files. For more information, see <xref:mvc/models/file-uploads#security-considerations>.
Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/fundamentals/dependency-injection.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ This article explains how Blazor apps can inject services into components.
> [!NOTE]
> We recommend reading <xref:fundamentals/dependency-injection> before reading this topic.

[!INCLUDE[](~/blazor/includes/location-client-and-server.md)]
[!INCLUDE[](~/blazor/includes/location-client-and-server-net31-or-later.md)]

## Default services

Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/fundamentals/handle-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ uid: blazor/fundamentals/handle-errors

This article describes how Blazor manages unhandled exceptions and how to develop apps that detect and handle errors.

[!INCLUDE[](~/blazor/includes/location-client-and-server.md)]
[!INCLUDE[](~/blazor/includes/location-client-and-server-net31-or-later.md)]

## Detailed errors during development

Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/fundamentals/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ uid: blazor/fundamentals/logging

This article explains Blazor app logging, including configuration and how to write log messages from Razor components.

[!INCLUDE[](~/blazor/includes/location-client-and-server.md)]
[!INCLUDE[](~/blazor/includes/location-client-and-server-net31-or-later.md)]

## Configuration

Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/fundamentals/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ uid: blazor/fundamentals/routing

This article explains how to manage Blazor app request routing and how to use the <xref:Microsoft.AspNetCore.Components.Routing.NavLink> component to create navigation links.

[!INCLUDE[](~/blazor/includes/location-client-and-server.md)]
[!INCLUDE[](~/blazor/includes/location-client-and-server-net31-or-later.md)]

> [!IMPORTANT]
> Code examples throughout this article show methods called on `Navigation`, which is an injected <xref:Microsoft.AspNetCore.Components.NavigationManager> in classes and components.
Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/fundamentals/signalr.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ uid: blazor/fundamentals/signalr

This article explains how to configure and manage SignalR connections in Blazor apps.

[!INCLUDE[](~/blazor/includes/location-client-and-server.md)]
[!INCLUDE[](~/blazor/includes/location-client-and-server-net31-or-later.md)]

<!--
UPDATE 8.0 It's not clear if we'll host guidance on adding SignalR
Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/fundamentals/static-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ uid: blazor/fundamentals/static-files

This article describes Blazor app configuration for serving static files.

[!INCLUDE[](~/blazor/includes/location-client-and-server.md)]
[!INCLUDE[](~/blazor/includes/location-client-and-server-net31-or-later.md)]

## Static File Middleware

Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/globalization-localization.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ In this article, *language* refers to selections made by a user in their browser
> [!NOTE]
> The code examples in this article adopt [nullable reference types (NRTs) and .NET compiler null-state static analysis](xref:migration/50-to-60#nullable-reference-types-nrts-and-net-compiler-null-state-static-analysis), which are supported in ASP.NET Core 6.0 or later. When targeting ASP.NET Core 5.0 or earlier, remove the null type designation (`?`) from the article's examples.

[!INCLUDE[](~/blazor/includes/location-client-and-server.md)]
[!INCLUDE[](~/blazor/includes/location-client-and-server-net31-or-later.md)]

## Globalization

Expand Down
Loading