Skip to content

Commit ab12963

Browse files
authored
Apply using statements and disposal section (#35566)
1 parent 42dd95b commit ab12963

9 files changed

Lines changed: 55 additions & 33 deletions

File tree

aspnetcore/blazor/call-web-api.md

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,27 @@ The solution includes a demonstration of obtaining weather data securely via an
364364
365365
:::moniker-end
366366
367+
## Disposal of `HttpRequestMessage`, `HttpResponseMessage`, and `HttpClient`
368+
369+
An <xref:System.Net.Http.HttpRequestMessage> without a body doesn't require explicit disposal with a [`using` declaration (C# 8 or later)](/dotnet/csharp/language-reference/proposals/csharp-8.0/using) or a `using` block (earlier than C# 8), but we recommend disposing with every use for the following reasons:
370+
371+
* It provides a performance improvement by avoiding finalizers.
372+
* It hardens the code for the future in case a request body is ever added to a <xref:System.Net.Http.HttpRequestMessage> that didn't initially have one.
373+
* It potentially avoids functional issues if a delegating handler expects <xref:System.IDisposable.Dispose%2A>/<xref:System.IAsyncDisposable.DisposeAsync%2A> to be called.
374+
* It's simpler to apply a general rule everywhere than trying to remember the specific cases when it matters.
375+
376+
Always dispose of <xref:System.Net.Http.HttpResponseMessage> instances.
377+
378+
***Never*** dispose of <xref:System.Net.Http.HttpClient> instances created by calling <xref:System.Net.Http.IHttpClientFactory.CreateClient%2A> because they're managed by the framework.
379+
380+
Example:
381+
382+
```csharp
383+
using var request = new HttpRequestMessage(HttpMethod.Get, "/weather-forecast");
384+
var client = clientFactory.CreateClient("ExternalApi");
385+
using var response = await client.SendAsync(request);
386+
```
387+
367388
## Client-side scenarios for calling external web APIs
368389
369390
Client-based components call external web APIs using <xref:System.Net.Http.HttpClient> instances, typically created with a preconfigured <xref:System.Net.Http.HttpClient> registered in the `Program` file:
@@ -411,12 +432,12 @@ else
411432
412433
protected override async Task OnInitializedAsync()
413434
{
414-
var request = new HttpRequestMessage(HttpMethod.Get,
435+
using var request = new HttpRequestMessage(HttpMethod.Get,
415436
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
416437
request.Headers.Add("Accept", "application/vnd.github.v3+json");
417438
request.Headers.Add("User-Agent", "HttpClientFactory-Sample");
418439
419-
var response = await Client.SendAsync(request);
440+
using var response = await Client.SendAsync(request);
420441
421442
if (response.IsSuccessStatusCode)
422443
{
@@ -471,10 +492,10 @@ Even if you call <xref:Microsoft.AspNetCore.Components.WebAssembly.Http.WebAssem
471492
472493
var urlEncodedRequestUri = WebUtility.UrlEncode("{REQUEST URI}");
473494
474-
var request = new HttpRequestMessage(HttpMethod.Get,
495+
using var request = new HttpRequestMessage(HttpMethod.Get,
475496
$"https://corsproxy.io/?{urlEncodedRequestUri}");
476497
477-
var response = await client.SendAsync(request);
498+
using var response = await client.SendAsync(request);
478499
479500
...
480501
}
@@ -528,14 +549,14 @@ else
528549
529550
protected override async Task OnInitializedAsync()
530551
{
531-
var request = new HttpRequestMessage(HttpMethod.Get,
552+
using var request = new HttpRequestMessage(HttpMethod.Get,
532553
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
533554
request.Headers.Add("Accept", "application/vnd.github.v3+json");
534555
request.Headers.Add("User-Agent", "HttpClientFactory-Sample");
535556
536557
var client = ClientFactory.CreateClient();
537558
538-
var response = await client.SendAsync(request);
559+
using var response = await client.SendAsync(request);
539560
540561
if (response.IsSuccessStatusCode)
541562
{
@@ -639,11 +660,11 @@ In the following file upload example:
639660
* `Http` is the <xref:System.Net.Http.HttpClient>.
640661
641662
```csharp
642-
var request = new HttpRequestMessage(HttpMethod.Post, "/Filesave");
663+
using var request = new HttpRequestMessage(HttpMethod.Post, "/Filesave");
643664
request.SetBrowserRequestStreamingEnabled(true);
644665
request.Content = content;
645666
646-
var response = await Http.SendAsync(request);
667+
using var response = await Http.SendAsync(request);
647668
```
648669
649670
Streaming requests:
@@ -797,7 +818,7 @@ As of C# 11 (.NET 7), you can compose a JSON string as a [raw string literal](/d
797818
<xref:System.Net.Http.Json.HttpClientJsonExtensions.PatchAsJsonAsync%2A> returns an <xref:System.Net.Http.HttpResponseMessage>. To deserialize the JSON content from the response message, use the <xref:System.Net.Http.Json.HttpContentJsonExtensions.ReadFromJsonAsync%2A> extension method. The following example reads JSON todo item data as an array. An empty array is created if no item data is returned by the method, so `content` isn't null after the statement executes:
798819
799820
```csharp
800-
var response = await Http.PatchAsJsonAsync(...);
821+
using var response = await Http.PatchAsJsonAsync(...);
801822
var content = await response.Content.ReadFromJsonAsync<TodoItem[]>() ??
802823
Array.Empty<TodoItem>();
803824
```
@@ -1180,7 +1201,7 @@ For a demonstration, see <xref:blazor/security/webassembly/standalone-with-ident
11801201
When composing an <xref:System.Net.Http.HttpRequestMessage>, set the browser request credentials and header directly:
11811202
11821203
```csharp
1183-
var request = new HttpRequestMessage() { ... };
1204+
using var request = new HttpRequestMessage() { ... };
11841205
11851206
request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
11861207
request.Headers.Add("X-Requested-With", [ "XMLHttpRequest" ]);
@@ -1216,7 +1237,7 @@ request.Headers.Add("X-Requested-With", [ "XMLHttpRequest" ]);
12161237
12171238
private async Task PostRequest()
12181239
{
1219-
var request = new HttpRequestMessage()
1240+
using var request = new HttpRequestMessage()
12201241
{
12211242
Method = new HttpMethod("POST"),
12221243
RequestUri = new Uri("https://localhost:10000/todoitems"),
@@ -1238,7 +1259,7 @@ request.Headers.Add("X-Requested-With", [ "XMLHttpRequest" ]);
12381259
request.Content.Headers.TryAddWithoutValidation(
12391260
"x-custom-header", "value");
12401261
1241-
var response = await Http.SendAsync(request);
1262+
using var response = await Http.SendAsync(request);
12421263
var responseStatusCode = response.StatusCode;
12431264
12441265
responseBody = await response.Content.ReadAsStringAsync();
@@ -1301,8 +1322,9 @@ request.SetBrowserResponseStreamingEnabled(true);
13011322
By default, [`HttpCompletionOption.ResponseContentRead`](xref:System.Net.Http.HttpCompletionOption) is set, which results in the <xref:System.Net.Http.HttpClient> completing after reading the entire response, including the content. In order to be able to use the <xref:Microsoft.AspNetCore.Components.WebAssembly.Http.WebAssemblyHttpRequestMessageExtensions.SetBrowserResponseStreamingEnabled%2A> option on large files, set [`HttpCompletionOption.ResponseHeadersRead`](xref:System.Net.Http.HttpCompletionOption) to avoid caching the file's content in memory:
13021323
13031324
```diff
1304-
- var response = await Http.SendAsync(request);
1305-
+ var response = await Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
1325+
- using var response = await Http.SendAsync(request);
1326+
+ using var response = await Http.SendAsync(request,
1327+
+ HttpCompletionOption.ResponseHeadersRead);
13061328
```
13071329
13081330
:::moniker-end
@@ -1405,9 +1427,9 @@ To add antiforgery support to an HTTP request, inject the `AntiforgeryStateProvi
14051427
private async Task OnSubmit()
14061428
{
14071429
var antiforgery = Antiforgery.GetAntiforgeryToken();
1408-
var request = new HttpRequestMessage(HttpMethod.Post, "action");
1430+
using var request = new HttpRequestMessage(HttpMethod.Post, "action");
14091431
request.Headers.Add("RequestVerificationToken", antiforgery.RequestToken);
1410-
var response = await client.SendAsync(request);
1432+
using var response = await client.SendAsync(request);
14111433
...
14121434
}
14131435
```

aspnetcore/blazor/components/quickgrid.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ The <xref:Microsoft.AspNetCore.Components.QuickGrid.GridItemsProvider%601> conve
446446
{ "limit", req.Count },
447447
});
448448
449-
var response = await Http.GetFromJsonAsync<FoodRecallQueryResult>(
449+
using var response = await Http.GetFromJsonAsync<FoodRecallQueryResult>(
450450
url, req.CancellationToken);
451451
452452
return GridItemsProviderResult.From(

aspnetcore/blazor/file-uploads.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -709,11 +709,11 @@ In a Blazor Web App, add the <xref:Microsoft.AspNetCore.Components.WebAssembly.H
709709
710710
if (upload)
711711
{
712-
var request = new HttpRequestMessage(HttpMethod.Post, "/Filesave");
712+
using var request = new HttpRequestMessage(HttpMethod.Post, "/Filesave");
713713
request.SetBrowserRequestStreamingEnabled(true);
714714
request.Content = content;
715715
716-
var response = await Http.SendAsync(request);
716+
using var response = await Http.SendAsync(request);
717717
718718
var newUploadResults = await response.Content
719719
.ReadFromJsonAsync<IList<UploadResult>>();

aspnetcore/blazor/forms/validation.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,7 @@ In the following component, update the namespace of the **`Shared`** project (`@
744744
745745
try
746746
{
747-
var response = await Http.PostAsJsonAsync<Starship>(
747+
using var response = await Http.PostAsJsonAsync<Starship>(
748748
"StarshipValidation", (Starship)editContext.Model);
749749
750750
var errors = await response.Content
@@ -888,7 +888,7 @@ The preceding example sets the base address with `builder.HostEnvironment.BaseAd
888888
889889
try
890890
{
891-
var response = await Http.PostAsJsonAsync<Starship>(
891+
using var response = await Http.PostAsJsonAsync<Starship>(
892892
"StarshipValidation", (Starship)editContext.Model);
893893
894894
var errors = await response.Content

aspnetcore/blazor/hybrid/security/maui-blazor-web-identity.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ In `MauiBlazorWeb/Services/MauiAuthenticationStateProvider.cs`:
162162
// Call the Login endpoint and pass the email and password
163163
var httpClient = HttpClientHelper.GetHttpClient();
164164
var loginData = new { loginModel.Email, loginModel.Password };
165-
var response = await httpClient.PostAsJsonAsync(HttpClientHelper.LoginUrl,
165+
using var response = await httpClient.PostAsJsonAsync(HttpClientHelper.LoginUrl,
166166
loginData);
167167

168168
LoginStatus =

aspnetcore/blazor/security/webassembly/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ Even if you call <xref:Microsoft.AspNetCore.Components.WebAssembly.Http.WebAssem
5858
5959
var urlEncodedRequestUri = WebUtility.UrlEncode("{REQUEST URI}");
6060
61-
var request = new HttpRequestMessage(HttpMethod.Get,
61+
using var request = new HttpRequestMessage(HttpMethod.Get,
6262
$"https://corsproxy.io/?{urlEncodedRequestUri}");
6363
64-
var response = await client.SendAsync(request);
64+
using var response = await client.SendAsync(request);
6565
6666
...
6767
}

aspnetcore/blazor/security/webassembly/standalone-with-identity/account-confirmation-and-password-recovery.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ public async Task<bool> ForgotPasswordAsync(string email)
331331
{
332332
try
333333
{
334-
var result = await httpClient.PostAsJsonAsync(
334+
using var result = await httpClient.PostAsJsonAsync(
335335
"forgotPassword", new
336336
{
337337
email
@@ -354,7 +354,7 @@ public async Task<FormResult> ResetPasswordAsync(string email, string resetCode,
354354

355355
try
356356
{
357-
var result = await httpClient.PostAsJsonAsync(
357+
using var result = await httpClient.PostAsJsonAsync(
358358
"resetPassword", new
359359
{
360360
email,

aspnetcore/blazor/security/webassembly/standalone-with-identity/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Instead of using the default UI provided by ASP.NET Core Identity for SPA and Bl
2121
On the client, call the `/register` endpoint to register a user with their email address and password:
2222

2323
```csharp
24-
var result = await _httpClient.PostAsJsonAsync(
24+
using var result = await _httpClient.PostAsJsonAsync(
2525
"register", new
2626
{
2727
email,
@@ -32,7 +32,7 @@ var result = await _httpClient.PostAsJsonAsync(
3232
On the client, log in a user with cookie authentication using the `/login` endpoint with `useCookies` query string set to `true`:
3333

3434
```csharp
35-
var result = await _httpClient.PostAsJsonAsync(
35+
using var result = await _httpClient.PostAsJsonAsync(
3636
"login?useCookies=true", new
3737
{
3838
email,

aspnetcore/blazor/security/webassembly/standalone-with-identity/qrcodes-for-authenticator-apps.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ public async Task<FormResult> LoginAsync(string email, string password)
184184
{
185185
try
186186
{
187-
var result = await httpClient.PostAsJsonAsync(
187+
using var result = await httpClient.PostAsJsonAsync(
188188
"login?useCookies=true", new
189189
{
190190
email,
@@ -199,7 +199,7 @@ public async Task<FormResult> LoginAsync(string email, string password)
199199
}
200200
else if (result.StatusCode == HttpStatusCode.Unauthorized)
201201
{
202-
var responseJson = await result.Content.ReadAsStringAsync();
202+
using var responseJson = await result.Content.ReadAsStringAsync();
203203
var response = JsonSerializer.Deserialize<LoginResponse>(
204204
responseJson, jsonSerializerOptions);
205205

@@ -236,7 +236,7 @@ public async Task<FormResult> LoginTwoFactorCodeAsync(
236236
{
237237
try
238238
{
239-
var result = await httpClient.PostAsJsonAsync(
239+
using var result = await httpClient.PostAsJsonAsync(
240240
"login?useCookies=true", new
241241
{
242242
email,
@@ -274,7 +274,7 @@ public async Task<FormResult> LoginTwoFactorRecoveryCodeAsync(string email,
274274
{
275275
try
276276
{
277-
var result = await httpClient.PostAsJsonAsync(
277+
using var result = await httpClient.PostAsJsonAsync(
278278
"login?useCookies=true", new
279279
{
280280
email,
@@ -318,7 +318,7 @@ public async Task<TwoFactorResponse> TwoFactorRequestAsync(TwoFactorRequest twoF
318318
string[] defaultDetail =
319319
[ "An unknown error prevented two-factor authentication." ];
320320

321-
var response = await httpClient.PostAsJsonAsync("manage/2fa", twoFactorRequest,
321+
using var response = await httpClient.PostAsJsonAsync("manage/2fa", twoFactorRequest,
322322
jsonSerializerOptions);
323323

324324
// successful?

0 commit comments

Comments
 (0)