ASP.NET Core Blazor에서 웹 API 호출
이 문서에서는 Blazor 앱에서 웹 API를 호출하는 방법을 설명합니다.
참고
이 문서는 Web API 호출을 위한 Blazor WebAssembly 적용 범위를 로드했습니다. Blazor Server 적용 범위는 다음과 같은 주제를 해결합니다.
HttpClient팩터리 인프라를 사용하여 앱에HttpClient를 제공합니다.- Blazor Server 앱과 관련된 CORS(원본 간 리소스 공유).
- 웹 API 액세스 테스트를 위한 Blazor 프레임워크 구성 요소 예제
- Web API를 호출하는 Blazor Server 앱을 개발하기 위한 추가 리소스.
Blazor WebAssembly 앱은 원본 서버로 다시 요청하는 데 초점을 맞춘 미리 구성된 HttpClient 서비스를 사용하여 웹 API를 호출합니다. 개발자 코드에서 다른 웹 API에 대한 추가 HttpClient 서비스 구성을 만들 수 있습니다. 요청은 BlazorJSON 도우미 또는 HttpRequestMessage를 사용하여 구성됩니다. 요청에는 Fetch API 옵션 구성이 포함될 수 있습니다.
이 문서의 예
이 문서의 구성 요소 예제에서는 가상의 할 일 목록 웹 API를 사용하여 서버에서 할 일 항목을 CRUD(만들고, 읽고, 업데이트하고, 삭제)합니다. 예제는 다음 할 일 항목 데이터를 저장하는 TodoItem 클래스를 기준으로 합니다.
- ID(
Id,long): 항목의 고유한 ID입니다. - 이름(
Name,string): 항목의 이름입니다. - 상태(
IsComplete,bool): 할 일 항목이 완료되었는지 여부를 나타냅니다.
예제를 테스트 앱에 빌드하는 경우 이 문서의 예제와 함께 다음 TodoItem 클래스를 사용합니다.
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
서버 쪽 Web API를 만드는 방법에 대한 지침은 자습서: ASP.NET Core을 사용하여 Web API 만들기를 참조하세요. CORS(원본 간 리소스 공유)에 대한 내용은 이 문서 뒷부분에 나오는 CORS 지침을 참조하세요.
패키지
System.Net.Http.Json에 대한 패키지 참조를 추가합니다.
참고
.NET 앱에 패키지를 추가하는 지침은 패키지 사용 워크플로(NuGet 설명서)에서 패키지 설치 및 관리 아래의 문서를 참조하세요. NuGet.org에서 올바른 패키지 버전을 확인합니다.
HttpClient 서비스 추가
Program.cs에서 앱을 만드는 데 사용된 Blazor 프로젝트 템플릿에 아직 없는 경우 HttpClient 서비스를 추가합니다.
builder.Services.AddScoped(sp =>
new HttpClient
{
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});
HttpClient 및 JSON 도우미
HttpClient는 원본 서버에 대해 다시 요청을 수행하기 위해 미리 구성된 서비스로 사용할 수 있습니다.
HttpClient 및 JSON 도우미(System.Net.Http.Json.HttpClientJsonExtensions)는 타사 웹 API 엔드포인트를 호출하는 데에도 사용됩니다. HttpClient는 브라우저 Fetch API를 사용하여 구현되며 동일한 원본 정책(이 문서 뒷부분에서 설명함) 적용을 비롯한 제한 사항이 적용됩니다.
클라이언트의 기본 주소가 원래 서버의 주소로 설정됩니다. @inject 지시문을 사용하여 HttpClient 인스턴스를 구성 요소에 삽입합니다.
@using System.Net.Http
@inject HttpClient Http
GetFromJsonAsync, PutAsJsonAsync 및 PostAsJsonAsync를 포함하여 HttpClientJsonExtensions에 대한 액세스에 System.Net.Http.Json 네임스페이스를 사용합니다.
@using System.Net.Http.Json
JSON에서 GET(GetFromJsonAsync)
GetFromJsonAsync는 HTTP GET 요청을 보내고 JSON 응답 본문을 구문 분석하여 개체를 만듭니다.
다음 구성 요소 코드에서 todoItems는 구성 요소에 의해 표시됩니다. 구성 요소의 초기화가 완료되면 GetFromJsonAsync가 호출됩니다(OnInitializedAsync).
@using System.Net.Http
@using System.Net.Http.Json
@using System.Threading.Tasks
@inject HttpClient Http
@if (todoItems == null)
{
<p>No Todo Items found.</p>
}
else
{
<ul>
@foreach (var item in todoItems)
{
<li>@item.Name</li>
}
</ul>
}
@code {
private TodoItem[]? todoItems;
protected override async Task OnInitializedAsync() =>
todoItems = await Http.GetFromJsonAsync<TodoItem[]>("api/TodoItems");
}
JSON으로 POST(PostAsJsonAsync)
PostAsJsonAsync는 요청 본문에서 JSON으로 직렬화된 값을 포함하는 지정된 URI에 POST 요청을 보냅니다.
다음 구성 요소 코드에서 구성 요소의 바인딩된 요소는 newItemName을 제공합니다. AddItem 메서드는 <button> 요소를 선택하여 트리거됩니다.
@using System.Net.Http
@using System.Net.Http.Json
@using System.Threading.Tasks
@inject HttpClient Http
<input @bind="newItemName" placeholder="New Todo Item" />
<button @onclick="AddItem">Add</button>
@code {
private string? newItemName;
private async Task AddItem()
{
var addItem = new TodoItem { Name = newItemName, IsComplete = false };
await Http.PostAsJsonAsync("api/TodoItems", addItem);
}
}
PostAsJsonAsync를 호출하면 HttpResponseMessage가 반환됩니다. 응답 메시지에서 JSON 콘텐츠를 역직렬화하려면 ReadFromJsonAsync 확장 메서드를 사용합니다. 다음 예제에서는 JSON 날씨 데이터를 읽습니다.
var content = await response.Content.ReadFromJsonAsync<WeatherForecast>();
ON으로 JSPUT(PutAsJsonAsync)
PutAsJsonAsync는 JSON 인코딩 콘텐츠와 함께 HTTP PUT 요청을 보냅니다.
다음 구성 요소 코드에서 구성 요소의 바인딩된 요소는 Name 및 IsCompleted에 대한 editItem 값을 제공합니다. 항목의 Id는 UI의 다른 부분에서 해당 항목을 선택하고(표시되지 않음) EditItem을 호출하면 설정됩니다. SaveItem 메서드는 <button> 요소를 선택하여 트리거됩니다. 다음 예제에서는 간단히 하기 위해 todoItems 로드 과정은 표시하지 않습니다. 항목 로드 예제는 JSON에서 GET(GetFromJsonAsync) 섹션을 참조하세요.
@using System.Net.Http
@using System.Net.Http.Json
@using System.Threading.Tasks
@inject HttpClient Http
<input type="checkbox" @bind="editItem.IsComplete" />
<input @bind="editItem.Name" />
<button @onclick="SaveItem">Save</button>
@code {
private string? id;
private TodoItem editItem = new TodoItem();
private void EditItem(long id)
{
editItem = todoItems.Single(i => i.Id == id);
}
private async Task SaveItem() =>
await Http.PutAsJsonAsync($"api/TodoItems/{editItem.Id}", editItem);
}
PutAsJsonAsync를 호출하면 HttpResponseMessage가 반환됩니다. 응답 메시지에서 JSON 콘텐츠를 역직렬화하려면 ReadFromJsonAsync 확장 메서드를 사용합니다. 다음 예제에서는 JSON 날씨 데이터를 읽습니다.
var content = await response.Content.ReadFromJsonAsync<WeatherForecast>();
추가 확장 메서드
System.Net.Http는 HTTP 요청을 보내고 HTTP 응답을 받기 위한 추가 확장 메서드를 포함합니다. HttpClient.DeleteAsync는 웹 API에 HTTP DELETE 요청을 보내는 데 사용됩니다.
다음 구성 요소 코드에서 <button> 요소는 DeleteItem 메서드를 호출합니다. 바인딩된 <input> 요소는 삭제할 항목의 id를 제공합니다.
@using System.Net.Http
@using System.Threading.Tasks
@inject HttpClient Http
<input @bind="id" />
<button @onclick="DeleteItem">Delete</button>
@code {
private long id;
private async Task DeleteItem() =>
await Http.DeleteAsync($"api/TodoItems/{id}");
}
HttpClient를 IHttpClientFactory로 명명함
IHttpClientFactory 서비스 및 명명된 HttpClient의 구성이 지원됩니다.
앱에 Microsoft.Extensions.Http NuGet 패키지를 추가합니다.
참고
.NET 앱에 패키지를 추가하는 지침은 패키지 사용 워크플로(NuGet 설명서)에서 패키지 설치 및 관리 아래의 문서를 참조하세요. NuGet.org에서 올바른 패키지 버전을 확인합니다.
Program.cs의 경우
builder.Services.AddHttpClient("WebAPI", client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
다음 구성 요소 코드에서:
- IHttpClientFactory의 인스턴스는 명명된 HttpClient를 만듭니다.
- 명명된 HttpClient는 웹 API에서 JSON 날씨 예측 데이터에 대한 GET 요청을 실행하는 데 사용됩니다.
Pages/FetchDataViaFactory.razor:
@page "/fetch-data-via-factory"
@using System.Net.Http
@using System.Net.Http.Json
@using System.Threading.Tasks
@inject IHttpClientFactory ClientFactory
<h1>Fetch data via <code>IHttpClientFactory</code></h1>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<h2>Temperatures by Date</h2>
<ul>
@foreach (var forecast in forecasts)
{
<li>
@forecast.Date.ToShortDateString():
@forecast.TemperatureC ℃
@forecast.TemperatureF ℉
</li>
}
</ul>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
var client = ClientFactory.CreateClient("WebAPI");
forecasts = await client.GetFromJsonAsync<WeatherForecast[]>(
"WeatherForecast");
}
}
형식화된 HttpClient
형식화된 HttpClient는 앱의 기본값 또는 명명된 HttpClient 인스턴스를 사용하여 하나 이상의 웹 API 엔드포인트에서 데이터를 반환합니다.
WeatherForecastClient.cs:
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
public class WeatherForecastHttpClient
{
private readonly HttpClient http;
public WeatherForecastHttpClient(HttpClient http)
{
this.http = http;
}
public async Task<WeatherForecast[]> GetForecastAsync()
{
try
{
return await http.GetFromJsonAsync<WeatherForecast[]>(
"WeatherForecast");
}
catch
{
...
return new WeatherForecast[0];
}
}
}
Program.cs의 경우
builder.Services.AddHttpClient<WeatherForecastHttpClient>(client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
구성 요소는 형식화된 HttpClient를 삽입하여 웹 API를 호출합니다.
다음 구성 요소 코드에서:
- 앞의
WeatherForecastHttpClient인스턴스가 삽입되어 형식화된 HttpClient를 만듭니다. - 형식화된 HttpClient는 웹 API에서 JSON 날씨 예측 데이터에 대한 GET 요청을 실행하는 데 사용됩니다.
Pages/FetchDataViaTypedHttpClient.razor:
@page "/fetch-data-via-typed-httpclient"
@using System.Threading.Tasks
@inject WeatherForecastHttpClient Http
<h1>Fetch data via typed <code>HttpClient</code></h1>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<h2>Temperatures by Date</h2>
<ul>
@foreach (var forecast in forecasts)
{
<li>
@forecast.Date.ToShortDateString():
@forecast.TemperatureC ℃
@forecast.TemperatureF ℉
</li>
}
</ul>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await Http.GetForecastAsync();
}
}
Fetch API 요청 옵션이 있는 HttpClient 및 HttpRequestMessage
HttpClient(API 설명서) 및 HttpRequestMessage를 사용하여 요청을 사용자 지정할 수 있습니다. 예를 들어, HTTP 메서드 및 요청 헤더를 지정할 수 있습니다. 다음 구성 요소는 웹 API 엔드포인트에 대한 POST 요청을 만들고 응답 본문을 표시합니다.
Pages/TodoRequest.razor:
@page "/todo-request"
@using System.Net.Http
@using System.Net.Http.Headers
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject HttpClient Http
@inject IAccessTokenProvider TokenProvider
<h1>ToDo Request</h1>
<button @onclick="PostRequest">Submit POST request</button>
<p>Response body returned by the server:</p>
<p>@responseBody</p>
@code {
private string? responseBody;
private async Task PostRequest()
{
var requestMessage = new HttpRequestMessage()
{
Method = new HttpMethod("POST"),
RequestUri = new Uri("https://localhost:10000/api/TodoItems"),
Content =
JsonContent.Create(new TodoItem
{
Name = "My New Todo Item",
IsComplete = false
})
};
var tokenResult = await TokenProvider.RequestAccessToken();
if (tokenResult.TryGetToken(out var token))
{
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token.Value);
requestMessage.Content.Headers.TryAddWithoutValidation(
"x-custom-header", "value");
var response = await Http.SendAsync(requestMessage);
var responseStatusCode = response.StatusCode;
responseBody = await response.Content.ReadAsStringAsync();
}
}
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
}
Blazor WebAssembly의 HttpClient 구현에서는 Fetch API를 사용합니다. Fetch API를 사용하면 몇 가지 요청 관련 옵션을 구성할 수 있습니다. 옵션은 다음 표에 표시된 HttpRequestMessage 확장 메서드로 구성할 수 있습니다.
| 확장 메서드 | Fetch API 요청 속성 |
|---|---|
| SetBrowserRequestCache | cache |
| SetBrowserRequestCredentials | credentials |
| SetBrowserRequestIntegrity | integrity |
| SetBrowserRequestMode | mode |
일반적인 SetBrowserRequestOption 확장 메서드를 사용하여 추가 옵션을 설정합니다.
HTTP 응답은 일반적으로 응답 콘텐츠에서 동기 읽기를 지원할 수 있도록 버퍼링됩니다. 응답 스트리밍을 지원하도록 설정하려면 요청에서 SetBrowserResponseStreamingEnabled 확장 메서드를 사용합니다.
원본 간 요청에 자격 증명을 포함하려면 SetBrowserRequestCredentials 확장 메서드를 사용합니다.
requestMessage.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
Fetch API 옵션에 대한 자세한 내용은 MDN 웹 문서: WindowOrWorkerGlobalScope.fetch(): 매개 변수를 참조하세요.
웹 API 호출 예제
다음 예제에서는 웹 API를 호출합니다. 이 예제에는 자습서: ASP.NET Core를 사용하여 Web API 만들기 문서에 설명된 샘플 앱을 기준으로 실행 중인 웹 API가 필요합니다. 이 예제는 https://localhost:10000/api/TodoItems에서 웹 API에 요청합니다. 다른 웹 API 주소를 사용하는 경우 구성 요소의 @code 블록에서 ServiceEndpoint 상수 값을 업데이트합니다.
다음 예제는 http://localhost:5000 또는 https://localhost:5001에서 웹 API에 대해 CORS(원본 간 리소스 공유) 요청을 합니다. 웹 API 서비스의 Program.cs 파일에 다음 CORS 미들웨어 구성을 추가합니다.
app.UseCors(policy =>
policy.WithOrigins("http://localhost:5000", "https://localhost:5001")
.AllowAnyMethod()
.WithHeaders(HeaderNames.ContentType));
Blazor 앱에 필요한 경우 WithOrigins의 도메인과 포트를 조정합니다. 자세한 내용은 ASP.NET Core에서 CORS(원본 간 요청) 사용을 참조하세요.
기본적으로 ASP.NET Core 앱은 포트 5000(HTTP) 및 5001(HTTPS)을 사용합니다. 테스트를 위해 두 앱을 동일한 머신에서 동시에 실행하려면 웹 API 앱에 대해 다른 포트(예: 포트 10000)를 사용합니다. 포트 설정에 대한 자세한 내용은 ASP.NET Core Kestrel 웹 서버에 대한 엔드포인트 구성을 참조하세요.
Pages/CallWebAPI.razor:
@page "/call-web-api"
@inject HttpClient Http
<h1>Todo Items</h1>
@if (todoItems == null)
{
<p>No Todo Items found.</p>
}
else
{
<table class="table">
<thead>
<tr>
<th class="text-center">Complete</th>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>
<tr id="editRow" style="display:@editRowStyle">
<td class="text-center">
<input type="checkbox" @bind="editItem.IsComplete" />
</td>
<td>
<input @bind="editItem.Name" />
</td>
<td class="text-center">
<button class="btn btn-success" @onclick="SaveItem">
Save
</button>
<button class="btn btn-danger"
@onclick="@(() => editRowStyle = "none")">
Cancel
</button>
</td>
</tr>
@foreach (var item in todoItems)
{
<tr>
<td class="text-center">
@if (item.IsComplete)
{
<span>✔</span>
}
</td>
<td>@item.Name</td>
<td class="text-center">
<button class="btn btn-warning"
@onclick="@(() => EditItem(item.Id))">
Edit
</button>
<button class="btn btn-danger"
@onclick="@(async () => await DeleteItem(item.Id))">
Delete
</button>
</td>
</tr>
}
<tr id="addRow">
<td></td>
<td>
<input @bind="newItemName" placeholder="New Todo Item" />
</td>
<td class="text-center">
<button class="btn btn-success" @onclick="AddItem">Add</button>
</td>
</tr>
</tbody>
</table>
}
@code {
private const string ServiceEndpoint = "https://localhost:10000/api/TodoItems";
private TodoItem[]? todoItems;
private TodoItem editItem = new();
private string editRowStyle = "none";
private string? newItemName;
protected override async Task OnInitializedAsync() => await GetTodoItems();
private async Task GetTodoItems() =>
todoItems = await Http.GetFromJsonAsync<TodoItem[]>(ServiceEndpoint);
private void EditItem(long id)
{
if (todoItems is not null)
{
editItem = todoItems.Single(i => i.Id == id);
editRowStyle = "table-row";
}
}
private async Task AddItem()
{
var addItem = new TodoItem { Name = newItemName, IsComplete = false };
await Http.PostAsJsonAsync(ServiceEndpoint, addItem);
newItemName = string.Empty;
await GetTodoItems();
editRowStyle = "none";
}
private async Task SaveItem()
{
if (editItem is not null)
{
await Http.PutAsJsonAsync($"{ServiceEndpoint}/{editItem.Id}",
editItem);
}
await GetTodoItems();
editRowStyle = "none";
}
private async Task DeleteItem(long id)
{
await Http.DeleteAsync($"{ServiceEndpoint}/{id}");
await GetTodoItems();
editRowStyle = "none";
}
private class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
}
오류 처리
개발자 코드에서 웹 API 응답 오류가 발생할 때 처리합니다. 예를 들어 GetFromJsonAsync에는 application/json의 Content-Type을 가진 웹 API의 JSON 응답이 필요합니다. 응답이 JSON 형식이 아닌 경우 콘텐츠 유효성 검사는 NotSupportedException을 throw합니다.
다음 예제에서는 날씨 예측 데이터 요청에 대한 URI 엔드포인트의 철자가 잘못되었습니다. URI는 WeatherForecast에 대한 것이어야 Forecast에서 e 문자가 누락된 WeatherForcast로 호출에 표시됩니다.
GetFromJsonAsync 호출에서는 JSON이 반환되어야 하지만, 웹 API는 text/html의 Content-Type을 사용하여 처리되지 않은 예외에 대한 HTML을 반환합니다. /WeatherForcast에 대한 경로를 찾을 수 없고 미들웨어에서 요청에 대한 페이지 또는 보기를 제공할 수 없기 때문에 처리되지 않은 예외가 발생합니다.
클라이언트의 OnInitializedAsync에서 응답 콘텐츠가 JSON이 아닌 것으로 확인되면 NotSupportedException이 throw됩니다. catch 블록에서 예외가 catch됩니다. 여기서 사용자 지정 논리는 오류를 기록하거나 사용자에게 친숙한 오류 메시지를 표시할 수 있습니다.
Pages/FetchDataReturnsHTMLOnException.razor:
@page "/fetch-data-returns-html-on-exception"
@using System.Net.Http
@using System.Net.Http.Json
@using System.Threading.Tasks
@inject HttpClient Http
<h1>Fetch data but receive HTML on unhandled exception</h1>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<h2>Temperatures by Date</h2>
<ul>
@foreach (var forecast in forecasts)
{
<li>
@forecast.Date.ToShortDateString():
@forecast.TemperatureC ℃
@forecast.TemperatureF ℉
</li>
}
</ul>
}
<p>
@exceptionMessage
</p>
@code {
private WeatherForecast[]? forecasts;
private string? exceptionMessage;
protected override async Task OnInitializedAsync()
{
try
{
// The URI endpoint "WeatherForecast" is misspelled on purpose on the
// next line. See the preceding text for more information.
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForcast");
}
catch (NotSupportedException exception)
{
exceptionMessage = exception.Message;
}
}
}
참고
앞의 예제는 데모용입니다. 엔드포인트가 없거나 서버에서 처리되지 않은 예외가 발생한 경우에도 JSON을 반환하도록 웹 API를 구성할 수 있습니다.
자세한 내용은 ASP.NET Core Blazor 앱의 오류 처리를 참조하세요.
참고
이 문서는 Web API 호출을 위한 Blazor Server 적용 범위를 로드했습니다. Blazor WebAssembly 적용 범위는 다음과 같은 주제를 해결합니다.
- 웹 API를 호출하여 할 일 목록 항목을 만들고, 읽고, 업데이트하고, 삭제하는 클라이언트 쪽 WebAssembly 앱을 기준으로 하는 Blazor WebAssembly 예제
System.Net.Http.Json패키지HttpClient서비스 구성HttpClient및 JSON 도우미(GetFromJsonAsync,PostAsJsonAsync,PutAsJsonAsync,DeleteAsync)IHttpClientFactory서비스 및 명명된HttpClient의 구성- 형식화된
HttpClient - 요청을 사용자 지정하기 위한
HttpClient및HttpRequestMessage - CORS(원본 간 리소스 공유)를 사용하고 CORS가 Blazor WebAssembly 앱과 관련된 방식을 보여 주는 웹 API 예제
- 개발자 코드에서 Web API 응답 오류를 처리하는 방법
- 웹 API 액세스 테스트를 위한 Blazor 프레임워크 구성 요소 예제
- Web API를 호출하는 Blazor WebAssembly 앱을 개발하기 위한 추가 리소스.
Blazor Server 앱은 일반적으로 IHttpClientFactory를 사용하여 만드는 HttpClient 인스턴스를 사용하여 웹 API를 호출합니다. Blazor Server에 적용되는 지침은 ASP.NET Core에서 IHttpClientFactory를 사용하여 HTTP 요청 만들기를 참조하세요.
Blazor Server 앱은 기본적으로 HttpClient 서비스를 포함하지 않습니다. HttpClient 팩터리 인프라를 사용하여 앱에 HttpClient를 제공합니다.
Program.cs의 경우
builder.Services.AddHttpClient();
다음 Blazor ServerRazor 구성 요소는 GitHub 분기에 대한 Web API 요청을 ASP.NET Core에서 IHttpClientFactory를 사용하여 HTTP 요청 만들기 문서의 기본 사용법 예제와 유사하게 만듭니다.
Pages/CallWebAPI.razor:
@page "/call-web-api"
@using System.Text.Json
@using System.Text.Json.Serialization;
@inject IHttpClientFactory ClientFactory
<h1>Call web API from a Blazor Server Razor component</h1>
@if (getBranchesError)
{
<p>Unable to get branches from GitHub. Please try again later.</p>
}
else
{
<ul>
@foreach (var branch in branches)
{
<li>@branch.Name</li>
}
</ul>
}
@code {
private IEnumerable<GitHubBranch> branches = Array.Empty<GitHubBranch>();
private bool getBranchesError;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
protected override async Task OnInitializedAsync()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
request.Headers.Add("Accept", "application/vnd.github.v3+json");
request.Headers.Add("User-Agent", "HttpClientFactory-Sample");
var client = ClientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
branches = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(responseStream);
}
else
{
getBranchesError = true;
}
shouldRender = true;
}
public class GitHubBranch
{
[JsonPropertyName("name")]
public string Name { get; set; }
}
}
추가 작업 예제는 ASP.NET Core Blazor 파일 업로드 문서에서 웹 API 컨트롤러에 파일을 업로드하는 Blazor Server 파일 업로드 예제를 참조하세요.
CORS(원본 간 리소스 공유)
브라우저 보안 때문에 웹 페이지에서 해당 웹 페이지를 제공한 도메인이 아닌 다른 도메인에 요청을 수행하는 것이 제한됩니다. 이러한 제한 사항을 동일 원본 정책이라고 합니다. 동일 원본 정책은 악성 사이트에서 다른 사이트의 중요한 데이터를 읽는 것을 제한합니다(차단하지는 않음). 브라우저에서 원본이 다른 엔드포인트로의 요청을 만들려면 엔드포인트가 CORS(원본 간 리소스 공유)를 사용하도록 설정해야 합니다.
Blazor WebAssembly 앱의 CORS 요청에 대한 자세한 내용은 ASP.NET Core Blazor WebAssembly 추가 보안 시나리오를 참조하세요.
CORS에 대한 내용은 ASP.NET Core에서 CORS(원본 간 요청) 사용을 참조하세요. 문서의 예제는 Blazor WebAssembly 앱에 직접 관련되지 않지만 일반적인 CORS 개념을 배우는 데 유용합니다.
자세한 내용은 ASP.NET Core에서 CORS(원본 간 요청) 사용을 참조하세요.
웹 API 액세스 테스트를 위한 Blazor 프레임워크 구성 요소 예제
Firefox Browser Developer 및 Postman과 같은 다양한 네트워크 도구를 웹 API 백 엔드 앱을 테스트하는 데 공개적으로 사용할 수 있습니다. Blazor 프레임워크의 참조 원본에는 테스트에 유용한 HttpClient 테스트 자산이 포함됩니다.
dotnet/aspnetcore GitHub 리포지토리의 HttpClientTest 자산
추가 자료
- ASP.NET Core Blazor WebAssembly 추가 보안 시나리오: 보안 웹 API 요청을 만드는 데 HttpClient를 사용하는 방법에 대한 설명을 포함합니다.
- ASP.NET Core에서 CORS(원본 간 요청) 사용: 콘텐츠가 Blazor WebAssembly 앱이 아닌 ASP.NET Core 앱에 적용되기는 하지만, 이 문서에서는 일반적인 CORS 개념을 설명합니다.
- W3C의 CORS(원본 간 리소스 공유)
- Fetch API
- ASP.NET Core Blazor Server 추가 보안 시나리오: 보안 웹 API 요청을 만드는 데 HttpClient를 사용하는 방법에 대한 설명을 포함합니다.
- ASP.NET Core에서 IHttpClientFactory를 사용하여 HTTP 요청 만들기
- ASP.NET Core에서 HTTPS 적용
- ASP.NET Core에서 CORS(원본 간 요청) 사용
- Kestrel HTTPS 엔드포인트 구성
- W3C의 CORS(원본 간 리소스 공유)
참고
이 문서는 Web API 호출을 위한 Blazor WebAssembly 적용 범위를 로드했습니다. Blazor Server 적용 범위는 다음과 같은 주제를 해결합니다.
HttpClient팩터리 인프라를 사용하여 앱에HttpClient를 제공합니다.- Blazor Server 앱과 관련된 CORS(원본 간 리소스 공유).
- 웹 API 액세스 테스트를 위한 Blazor 프레임워크 구성 요소 예제
- Web API를 호출하는 Blazor Server 앱을 개발하기 위한 추가 리소스.
Blazor WebAssembly 앱은 원본 서버로 다시 요청하는 데 초점을 맞춘 미리 구성된 HttpClient 서비스를 사용하여 웹 API를 호출합니다. 개발자 코드에서 다른 웹 API에 대한 추가 HttpClient 서비스 구성을 만들 수 있습니다. 요청은 BlazorJSON 도우미 또는 HttpRequestMessage를 사용하여 구성됩니다. 요청에는 Fetch API 옵션 구성이 포함될 수 있습니다.
이 문서의 예
이 문서의 구성 요소 예제에서는 가상의 할 일 목록 웹 API를 사용하여 서버에서 할 일 항목을 CRUD(만들고, 읽고, 업데이트하고, 삭제)합니다. 예제는 다음 할 일 항목 데이터를 저장하는 TodoItem 클래스를 기준으로 합니다.
- ID(
Id,long): 항목의 고유한 ID입니다. - 이름(
Name,string): 항목의 이름입니다. - 상태(
IsComplete,bool): 할 일 항목이 완료되었는지 여부를 나타냅니다.
예제를 테스트 앱에 빌드하는 경우 이 문서의 예제와 함께 다음 TodoItem 클래스를 사용합니다.
public class TodoItem
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
}
서버 쪽 Web API를 만드는 방법에 대한 지침은 자습서: ASP.NET Core을 사용하여 Web API 만들기를 참조하세요. CORS(원본 간 리소스 공유)에 대한 내용은 이 문서 뒷부분에 나오는 CORS 지침을 참조하세요.
패키지
System.Net.Http.Json에 대한 패키지 참조를 추가합니다.
참고
.NET 앱에 패키지를 추가하는 지침은 패키지 사용 워크플로(NuGet 설명서)에서 패키지 설치 및 관리 아래의 문서를 참조하세요. NuGet.org에서 올바른 패키지 버전을 확인합니다.
HttpClient 서비스 추가
Program.cs에서 앱을 만드는 데 사용된 Blazor 프로젝트 템플릿에 아직 없는 경우 HttpClient 서비스를 추가합니다.
builder.Services.AddScoped(sp =>
new HttpClient
{
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});
HttpClient 및 JSON 도우미
HttpClient는 원본 서버에 대해 다시 요청을 수행하기 위해 미리 구성된 서비스로 사용할 수 있습니다.
HttpClient 및 JSON 도우미(System.Net.Http.Json.HttpClientJsonExtensions)는 타사 웹 API 엔드포인트를 호출하는 데에도 사용됩니다. HttpClient는 브라우저 Fetch API를 사용하여 구현되며 동일한 원본 정책(이 문서 뒷부분에서 설명함) 적용을 비롯한 제한 사항이 적용됩니다.
클라이언트의 기본 주소가 원래 서버의 주소로 설정됩니다. @inject 지시문을 사용하여 HttpClient 인스턴스를 구성 요소에 삽입합니다.
@using System.Net.Http
@inject HttpClient Http
GetFromJsonAsync, PutAsJsonAsync 및 PostAsJsonAsync를 포함하여 HttpClientJsonExtensions에 대한 액세스에 System.Net.Http.Json 네임스페이스를 사용합니다.
@using System.Net.Http.Json
JSON에서 GET(GetFromJsonAsync)
GetFromJsonAsync는 HTTP GET 요청을 보내고 JSON 응답 본문을 구문 분석하여 개체를 만듭니다.
다음 구성 요소 코드에서 todoItems는 구성 요소에 의해 표시됩니다. 구성 요소의 초기화가 완료되면 GetFromJsonAsync가 호출됩니다(OnInitializedAsync).
@using System.Net.Http
@using System.Net.Http.Json
@using System.Threading.Tasks
@inject HttpClient Http
@if (todoItems == null)
{
<p>No Todo Items found.</p>
}
else
{
<ul>
@foreach (var item in todoItems)
{
<li>@item.Name</li>
}
</ul>
}
@code {
private TodoItem[] todoItems;
protected override async Task OnInitializedAsync() =>
todoItems = await Http.GetFromJsonAsync<TodoItem[]>("api/TodoItems");
}
JSON으로 POST(PostAsJsonAsync)
PostAsJsonAsync는 요청 본문에서 JSON으로 직렬화된 값을 포함하는 지정된 URI에 POST 요청을 보냅니다.
다음 구성 요소 코드에서 구성 요소의 바인딩된 요소는 newItemName을 제공합니다. AddItem 메서드는 <button> 요소를 선택하여 트리거됩니다.
@using System.Net.Http
@using System.Net.Http.Json
@using System.Threading.Tasks
@inject HttpClient Http
<input @bind="newItemName" placeholder="New Todo Item" />
<button @onclick="AddItem">Add</button>
@code {
private string newItemName;
private async Task AddItem()
{
var addItem = new TodoItem { Name = newItemName, IsComplete = false };
await Http.PostAsJsonAsync("api/TodoItems", addItem);
}
}
PostAsJsonAsync를 호출하면 HttpResponseMessage가 반환됩니다. 응답 메시지에서 JSON 콘텐츠를 역직렬화하려면 ReadFromJsonAsync 확장 메서드를 사용합니다. 다음 예제에서는 JSON 날씨 데이터를 읽습니다.
var content = await response.Content.ReadFromJsonAsync<WeatherForecast>();
ON으로 JSPUT(PutAsJsonAsync)
PutAsJsonAsync는 JSON 인코딩 콘텐츠와 함께 HTTP PUT 요청을 보냅니다.
다음 구성 요소 코드에서 구성 요소의 바인딩된 요소는 Name 및 IsCompleted에 대한 editItem 값을 제공합니다. 항목의 Id는 UI의 다른 부분에서 해당 항목을 선택하고(표시되지 않음) EditItem을 호출하면 설정됩니다. SaveItem 메서드는 <button> 요소를 선택하여 트리거됩니다.
@using System.Net.Http
@using System.Net.Http.Json
@using System.Threading.Tasks
@inject HttpClient Http
<input type="checkbox" @bind="editItem.IsComplete" />
<input @bind="editItem.Name" />
<button @onclick="SaveItem">Save</button>
@code {
private string id;
private TodoItem editItem = new TodoItem();
private void EditItem(long id)
{
editItem = todoItems.Single(i => i.Id == id);
}
private async Task SaveItem() =>
await Http.PutAsJsonAsync($"api/TodoItems/{editItem.Id}", editItem);
}
PutAsJsonAsync를 호출하면 HttpResponseMessage가 반환됩니다. 응답 메시지에서 JSON 콘텐츠를 역직렬화하려면 ReadFromJsonAsync 확장 메서드를 사용합니다. 다음 예제에서는 JSON 날씨 데이터를 읽습니다.
var content = await response.Content.ReadFromJsonAsync<WeatherForecast>();
추가 확장 메서드
System.Net.Http는 HTTP 요청을 보내고 HTTP 응답을 받기 위한 추가 확장 메서드를 포함합니다. HttpClient.DeleteAsync는 웹 API에 HTTP DELETE 요청을 보내는 데 사용됩니다.
다음 구성 요소 코드에서 <button> 요소는 DeleteItem 메서드를 호출합니다. 바인딩된 <input> 요소는 삭제할 항목의 id를 제공합니다.
@using System.Net.Http
@using System.Threading.Tasks
@inject HttpClient Http
<input @bind="id" />
<button @onclick="DeleteItem">Delete</button>
@code {
private long id;
private async Task DeleteItem() =>
await Http.DeleteAsync($"api/TodoItems/{id}");
}
HttpClient를 IHttpClientFactory로 명명함
IHttpClientFactory 서비스 및 명명된 HttpClient의 구성이 지원됩니다.
앱에 Microsoft.Extensions.Http NuGet 패키지를 추가합니다.
참고
.NET 앱에 패키지를 추가하는 지침은 패키지 사용 워크플로(NuGet 설명서)에서 패키지 설치 및 관리 아래의 문서를 참조하세요. NuGet.org에서 올바른 패키지 버전을 확인합니다.
Program.cs의 경우
builder.Services.AddHttpClient("WebAPI", client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
다음 구성 요소 코드에서:
- IHttpClientFactory의 인스턴스는 명명된 HttpClient를 만듭니다.
- 명명된 HttpClient는 웹 API에서 JSON 날씨 예측 데이터에 대한 GET 요청을 실행하는 데 사용됩니다.
Pages/FetchDataViaFactory.razor:
@page "/fetch-data-via-factory"
@using System.Net.Http
@using System.Net.Http.Json
@using System.Threading.Tasks
@inject IHttpClientFactory ClientFactory
<h1>Fetch data via <code>IHttpClientFactory</code></h1>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<h2>Temperatures by Date</h2>
<ul>
@foreach (var forecast in forecasts)
{
<li>
@forecast.Date.ToShortDateString():
@forecast.TemperatureC ℃
@forecast.TemperatureF ℉
</li>
}
</ul>
}
@code {
private WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
var client = ClientFactory.CreateClient("WebAPI");
forecasts = await client.GetFromJsonAsync<WeatherForecast[]>(
"WeatherForecast");
}
}
형식화된 HttpClient
형식화된 HttpClient는 앱의 기본값 또는 명명된 HttpClient 인스턴스를 사용하여 하나 이상의 웹 API 엔드포인트에서 데이터를 반환합니다.
WeatherForecastClient.cs:
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
public class WeatherForecastHttpClient
{
private readonly HttpClient http;
public WeatherForecastHttpClient(HttpClient http)
{
this.http = http;
}
public async Task<WeatherForecast[]> GetForecastAsync()
{
try
{
return await http.GetFromJsonAsync<WeatherForecast[]>(
"WeatherForecast");
}
catch
{
...
return new WeatherForecast[0];
}
}
}
Program.cs의 경우
builder.Services.AddHttpClient<WeatherForecastHttpClient>(client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
구성 요소는 형식화된 HttpClient를 삽입하여 웹 API를 호출합니다.
다음 구성 요소 코드에서:
- 앞의
WeatherForecastHttpClient인스턴스가 삽입되어 형식화된 HttpClient를 만듭니다. - 형식화된 HttpClient는 웹 API에서 JSON 날씨 예측 데이터에 대한 GET 요청을 실행하는 데 사용됩니다.
Pages/FetchDataViaTypedHttpClient.razor:
@page "/fetch-data-via-typed-httpclient"
@using System.Threading.Tasks
@inject WeatherForecastHttpClient Http
<h1>Fetch data via typed <code>HttpClient</code></h1>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<h2>Temperatures by Date</h2>
<ul>
@foreach (var forecast in forecasts)
{
<li>
@forecast.Date.ToShortDateString():
@forecast.TemperatureC ℃
@forecast.TemperatureF ℉
</li>
}
</ul>
}
@code {
private WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await Http.GetForecastAsync();
}
}
Fetch API 요청 옵션이 있는 HttpClient 및 HttpRequestMessage
HttpClient(API 설명서) 및 HttpRequestMessage를 사용하여 요청을 사용자 지정할 수 있습니다. 예를 들어, HTTP 메서드 및 요청 헤더를 지정할 수 있습니다. 다음 구성 요소는 웹 API 엔드포인트에 대한 POST 요청을 만들고 응답 본문을 표시합니다.
Pages/TodoRequest.razor:
@page "/todo-request"
@using System.Net.Http
@using System.Net.Http.Headers
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject HttpClient Http
@inject IAccessTokenProvider TokenProvider
<h1>ToDo Request</h1>
<button @onclick="PostRequest">Submit POST request</button>
<p>Response body returned by the server:</p>
<p>@responseBody</p>
@code {
private string responseBody;
private async Task PostRequest()
{
var requestMessage = new HttpRequestMessage()
{
Method = new HttpMethod("POST"),
RequestUri = new Uri("https://localhost:10000/api/TodoItems"),
Content =
JsonContent.Create(new TodoItem
{
Name = "My New Todo Item",
IsComplete = false
})
};
var tokenResult = await TokenProvider.RequestAccessToken();
if (tokenResult.TryGetToken(out var token))
{
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token.Value);
requestMessage.Content.Headers.TryAddWithoutValidation(
"x-custom-header", "value");
var response = await Http.SendAsync(requestMessage);
var responseStatusCode = response.StatusCode;
responseBody = await response.Content.ReadAsStringAsync();
}
}
public class TodoItem
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
}
}
Blazor WebAssembly의 HttpClient 구현에서는 Fetch API를 사용합니다. Fetch API를 사용하면 몇 가지 요청 관련 옵션을 구성할 수 있습니다. 옵션은 다음 표에 표시된 HttpRequestMessage 확장 메서드로 구성할 수 있습니다.
| 확장 메서드 | Fetch API 요청 속성 |
|---|---|
| SetBrowserRequestCache | cache |
| SetBrowserRequestCredentials | credentials |
| SetBrowserRequestIntegrity | integrity |
| SetBrowserRequestMode | mode |
일반적인 SetBrowserRequestOption 확장 메서드를 사용하여 추가 옵션을 설정합니다.
HTTP 응답은 일반적으로 응답 콘텐츠에서 동기 읽기를 지원할 수 있도록 버퍼링됩니다. 응답 스트리밍을 지원하도록 설정하려면 요청에서 SetBrowserResponseStreamingEnabled 확장 메서드를 사용합니다.
원본 간 요청에 자격 증명을 포함하려면 SetBrowserRequestCredentials 확장 메서드를 사용합니다.
requestMessage.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
Fetch API 옵션에 대한 자세한 내용은 MDN 웹 문서: WindowOrWorkerGlobalScope.fetch(): 매개 변수를 참조하세요.
웹 API 호출 예제
다음 예제에서는 웹 API를 호출합니다. 이 예제에는 자습서: ASP.NET Core를 사용하여 Web API 만들기 문서에 설명된 샘플 앱을 기준으로 실행 중인 웹 API가 필요합니다. 이 예제는 https://localhost:10000/api/TodoItems에서 웹 API에 요청합니다. 다른 웹 API 주소를 사용하는 경우 구성 요소의 @code 블록에서 ServiceEndpoint 상수 값을 업데이트합니다.
다음 예제는 http://localhost:5000 또는 https://localhost:5001에서 웹 API에 대해 CORS(원본 간 리소스 공유) 요청을 합니다. 웹 API 서비스의 Startup.Configure 메서드에 다음 CORS 미들웨어 구성을 추가합니다.
app.UseCors(policy =>
policy.WithOrigins("http://localhost:5000", "https://localhost:5001")
.AllowAnyMethod()
.WithHeaders(HeaderNames.ContentType));
Blazor 앱에 필요한 경우 WithOrigins의 도메인과 포트를 조정합니다. 자세한 내용은 ASP.NET Core에서 CORS(원본 간 요청) 사용을 참조하세요.
기본적으로 ASP.NET Core 앱은 포트 5000(HTTP) 및 5001(HTTPS)을 사용합니다. 테스트를 위해 두 앱을 동일한 머신에서 동시에 실행하려면 웹 API 앱에 대해 다른 포트(예: 포트 10000)를 사용합니다. 포트 설정에 대한 자세한 내용은 ASP.NET Core Kestrel 웹 서버에 대한 엔드포인트 구성을 참조하세요.
Pages/CallWebAPI.razor:
@page "/call-web-api"
@inject HttpClient Http
<h1>Todo Items</h1>
@if (todoItems == null)
{
<p>No Todo Items found.</p>
}
else
{
<table class="table">
<thead>
<tr>
<th class="text-center">Complete</th>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>
<tr id="editRow" style="display:@editRowStyle">
<td class="text-center">
<input type="checkbox" @bind="editItem.IsComplete" />
</td>
<td>
<input @bind="editItem.Name" />
</td>
<td class="text-center">
<button class="btn btn-success" @onclick="SaveItem">
Save
</button>
<button class="btn btn-danger"
@onclick="@(() => editRowStyle = "none")">
Cancel
</button>
</td>
</tr>
@foreach (var item in todoItems)
{
<tr>
<td class="text-center">
@if (item.IsComplete)
{
<span>✔</span>
}
</td>
<td>@item.Name</td>
<td class="text-center">
<button class="btn btn-warning"
@onclick="@(() => EditItem(item.Id))">
Edit
</button>
<button class="btn btn-danger"
@onclick="@(async () => await DeleteItem(item.Id))">
Delete
</button>
</td>
</tr>
}
<tr id="addRow">
<td></td>
<td>
<input @bind="newItemName" placeholder="New Todo Item" />
</td>
<td class="text-center">
<button class="btn btn-success" @onclick="AddItem">Add</button>
</td>
</tr>
</tbody>
</table>
}
@code {
private const string ServiceEndpoint = "https://localhost:10000/api/TodoItems";
private TodoItem[] todoItems;
private TodoItem editItem = new();
private string editRowStyle = "none";
private string newItemName;
protected override async Task OnInitializedAsync() => await GetTodoItems();
private async Task GetTodoItems() =>
todoItems = await Http.GetFromJsonAsync<TodoItem[]>(ServiceEndpoint);
private void EditItem(long id)
{
editItem = todoItems.Single(i => i.Id == id);
editRowStyle = "table-row";
}
private async Task AddItem()
{
var addItem = new TodoItem { Name = newItemName, IsComplete = false };
await Http.PostAsJsonAsync(ServiceEndpoint, addItem);
newItemName = string.Empty;
await GetTodoItems();
editRowStyle = "none";
}
private async Task SaveItem()
{
await Http.PutAsJsonAsync($"{ServiceEndpoint}/{editItem.Id}", editItem);
await GetTodoItems();
editRowStyle = "none";
}
private async Task DeleteItem(long id)
{
await Http.DeleteAsync($"{ServiceEndpoint}/{id}");
await GetTodoItems();
editRowStyle = "none";
}
private class TodoItem
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
}
}
오류 처리
개발자 코드에서 웹 API 응답 오류가 발생할 때 처리합니다. 예를 들어 GetFromJsonAsync에는 application/json의 Content-Type을 가진 웹 API의 JSON 응답이 필요합니다. 응답이 JSON 형식이 아닌 경우 콘텐츠 유효성 검사는 NotSupportedException을 throw합니다.
다음 예제에서는 날씨 예측 데이터 요청에 대한 URI 엔드포인트의 철자가 잘못되었습니다. URI는 WeatherForecast에 대한 것이어야 Forecast에서 e 문자가 누락된 WeatherForcast로 호출에 표시됩니다.
GetFromJsonAsync 호출에서는 JSON이 반환되어야 하지만, 웹 API는 text/html의 Content-Type을 사용하여 처리되지 않은 예외에 대한 HTML을 반환합니다. /WeatherForcast에 대한 경로를 찾을 수 없고 미들웨어에서 요청에 대한 페이지 또는 보기를 제공할 수 없기 때문에 처리되지 않은 예외가 발생합니다.
클라이언트의 OnInitializedAsync에서 응답 콘텐츠가 JSON이 아닌 것으로 확인되면 NotSupportedException이 throw됩니다. catch 블록에서 예외가 catch됩니다. 여기서 사용자 지정 논리는 오류를 기록하거나 사용자에게 친숙한 오류 메시지를 표시할 수 있습니다.
Pages/FetchDataReturnsHTMLOnException.razor:
@page "/fetch-data-returns-html-on-exception"
@using System.Net.Http
@using System.Net.Http.Json
@using System.Threading.Tasks
@inject HttpClient Http
<h1>Fetch data but receive HTML on unhandled exception</h1>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<h2>Temperatures by Date</h2>
<ul>
@foreach (var forecast in forecasts)
{
<li>
@forecast.Date.ToShortDateString():
@forecast.TemperatureC ℃
@forecast.TemperatureF ℉
</li>
}
</ul>
}
<p>
@exceptionMessage
</p>
@code {
private WeatherForecast[] forecasts;
private string exceptionMessage;
protected override async Task OnInitializedAsync()
{
try
{
// The URI endpoint "WeatherForecast" is misspelled on purpose on the
// next line. See the preceding text for more information.
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForcast");
}
catch (NotSupportedException exception)
{
exceptionMessage = exception.Message;
}
}
}
참고
앞의 예제는 데모용입니다. 엔드포인트가 없거나 서버에서 처리되지 않은 예외가 발생한 경우에도 JSON을 반환하도록 웹 API를 구성할 수 있습니다.
자세한 내용은 ASP.NET Core Blazor 앱의 오류 처리를 참조하세요.
참고
이 문서는 Web API 호출을 위한 Blazor Server 적용 범위를 로드했습니다. Blazor WebAssembly 적용 범위는 다음과 같은 주제를 해결합니다.
- 웹 API를 호출하여 할 일 목록 항목을 만들고, 읽고, 업데이트하고, 삭제하는 클라이언트 쪽 WebAssembly 앱을 기준으로 하는 Blazor WebAssembly 예제
System.Net.Http.Json패키지HttpClient서비스 구성HttpClient및 JSON 도우미(GetFromJsonAsync,PostAsJsonAsync,PutAsJsonAsync,DeleteAsync)IHttpClientFactory서비스 및 명명된HttpClient의 구성- 형식화된
HttpClient - 요청을 사용자 지정하기 위한
HttpClient및HttpRequestMessage - CORS(원본 간 리소스 공유)를 사용하고 CORS가 Blazor WebAssembly 앱과 관련된 방식을 보여 주는 웹 API 예제
- 개발자 코드에서 Web API 응답 오류를 처리하는 방법
- 웹 API 액세스 테스트를 위한 Blazor 프레임워크 구성 요소 예제
- Web API를 호출하는 Blazor WebAssembly 앱을 개발하기 위한 추가 리소스.
Blazor Server 앱은 일반적으로 IHttpClientFactory를 사용하여 만드는 HttpClient 인스턴스를 사용하여 웹 API를 호출합니다. Blazor Server에 적용되는 지침은 ASP.NET Core에서 IHttpClientFactory를 사용하여 HTTP 요청 만들기를 참조하세요.
Blazor Server 앱은 기본적으로 HttpClient 서비스를 포함하지 않습니다. HttpClient 팩터리 인프라를 사용하여 앱에 HttpClient를 제공합니다.
Startup.cs의 Startup.ConfigureServices에서 다음을 수행합니다.
services.AddHttpClient();
다음 Blazor ServerRazor 구성 요소는 GitHub 분기에 대한 Web API 요청을 ASP.NET Core에서 IHttpClientFactory를 사용하여 HTTP 요청 만들기 문서의 기본 사용법 예제와 유사하게 만듭니다.
Pages/CallWebAPI.razor:
@page "/call-web-api"
@using System.Text.Json
@using System.Text.Json.Serialization;
@inject IHttpClientFactory ClientFactory
<h1>Call web API from a Blazor Server Razor component</h1>
@if (getBranchesError)
{
<p>Unable to get branches from GitHub. Please try again later.</p>
}
else
{
<ul>
@foreach (var branch in branches)
{
<li>@branch.Name</li>
}
</ul>
}
@code {
private IEnumerable<GitHubBranch> branches = Array.Empty<GitHubBranch>();
private bool getBranchesError;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
protected override async Task OnInitializedAsync()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
request.Headers.Add("Accept", "application/vnd.github.v3+json");
request.Headers.Add("User-Agent", "HttpClientFactory-Sample");
var client = ClientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
branches = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(responseStream);
}
else
{
getBranchesError = true;
}
shouldRender = true;
}
public class GitHubBranch
{
[JsonPropertyName("name")]
public string Name { get; set; }
}
}
추가 작업 예제는 ASP.NET Core Blazor 파일 업로드 문서에서 웹 API 컨트롤러에 파일을 업로드하는 Blazor Server 파일 업로드 예제를 참조하세요.
CORS(원본 간 리소스 공유)
브라우저 보안 때문에 웹 페이지에서 해당 웹 페이지를 제공한 도메인이 아닌 다른 도메인에 요청을 수행하는 것이 제한됩니다. 이러한 제한 사항을 동일 원본 정책이라고 합니다. 동일 원본 정책은 악성 사이트에서 다른 사이트의 중요한 데이터를 읽는 것을 제한합니다(차단하지는 않음). 브라우저에서 원본이 다른 엔드포인트로의 요청을 만들려면 엔드포인트가 CORS(원본 간 리소스 공유)를 사용하도록 설정해야 합니다.
Blazor WebAssembly 앱의 CORS 요청에 대한 자세한 내용은 ASP.NET Core Blazor WebAssembly 추가 보안 시나리오를 참조하세요.
CORS에 대한 내용은 ASP.NET Core에서 CORS(원본 간 요청) 사용을 참조하세요. 문서의 예제는 Blazor WebAssembly 앱에 직접 관련되지 않지만 일반적인 CORS 개념을 배우는 데 유용합니다.
자세한 내용은 ASP.NET Core에서 CORS(원본 간 요청) 사용을 참조하세요.
웹 API 액세스 테스트를 위한 Blazor 프레임워크 구성 요소 예제
Firefox Browser Developer 및 Postman과 같은 다양한 네트워크 도구를 웹 API 백 엔드 앱을 테스트하는 데 공개적으로 사용할 수 있습니다. Blazor 프레임워크의 참조 원본에는 테스트에 유용한 HttpClient 테스트 자산이 포함됩니다.
dotnet/aspnetcore GitHub 리포지토리의 HttpClientTest 자산
참고
.NET 참조 원본의 설명서 링크는 일반적으로 다음 릴리스의 .NET을 위한 현재 개발을 나타내는 리포지토리의 기본 분기를 로드합니다. 특정 릴리스를 위한 태그를 선택하려면 Switch branches or tags(분기 또는 태그 전환) 드롭다운 목록을 사용합니다. 자세한 내용은 ASP.NET Core 소스 코드(dotnet/AspNetCore.Docs #26205)의 버전 태그를 선택하는 방법을 참조하세요.
추가 자료
- ASP.NET Core Blazor WebAssembly 추가 보안 시나리오: 보안 웹 API 요청을 만드는 데 HttpClient를 사용하는 방법에 대한 설명을 포함합니다.
- ASP.NET Core에서 CORS(원본 간 요청) 사용: 콘텐츠가 Blazor WebAssembly 앱이 아닌 ASP.NET Core 앱에 적용되기는 하지만, 이 문서에서는 일반적인 CORS 개념을 설명합니다.
- W3C의 CORS(원본 간 리소스 공유)
- Fetch API
- ASP.NET Core Blazor Server 추가 보안 시나리오: 보안 웹 API 요청을 만드는 데 HttpClient를 사용하는 방법에 대한 설명을 포함합니다.
- ASP.NET Core에서 IHttpClientFactory를 사용하여 HTTP 요청 만들기
- ASP.NET Core에서 HTTPS 적용
- ASP.NET Core에서 CORS(원본 간 요청) 사용
- Kestrel HTTPS 엔드포인트 구성
- W3C의 CORS(원본 간 리소스 공유)
참고
이 문서는 Web API 호출을 위한 Blazor WebAssembly 적용 범위를 로드했습니다. Blazor Server 적용 범위는 다음과 같은 주제를 해결합니다.
HttpClient팩터리 인프라를 사용하여 앱에HttpClient를 제공합니다.- Blazor Server 앱과 관련된 CORS(원본 간 리소스 공유).
- 웹 API 액세스 테스트를 위한 Blazor 프레임워크 구성 요소 예제
- Web API를 호출하는 Blazor Server 앱을 개발하기 위한 추가 리소스.
Blazor WebAssembly 앱은 원본 서버로 다시 요청하는 데 초점을 맞춘 미리 구성된 HttpClient 서비스를 사용하여 웹 API를 호출합니다. 개발자 코드에서 다른 웹 API에 대한 추가 HttpClient 서비스 구성을 만들 수 있습니다. 요청은 BlazorJSON 도우미 또는 HttpRequestMessage를 사용하여 구성됩니다. 요청에는 Fetch API 옵션 구성이 포함될 수 있습니다.
이 문서의 예
이 문서의 구성 요소 예제에서는 가상의 할 일 목록 웹 API를 사용하여 서버에서 할 일 항목을 CRUD(만들고, 읽고, 업데이트하고, 삭제)합니다. 예제는 다음 할 일 항목 데이터를 저장하는 TodoItem 클래스를 기준으로 합니다.
- ID(
Id,long): 항목의 고유한 ID입니다. - 이름(
Name,string): 항목의 이름입니다. - 상태(
IsComplete,bool): 할 일 항목이 완료되었는지 여부를 나타냅니다.
예제를 테스트 앱에 빌드하는 경우 이 문서의 예제와 함께 다음 TodoItem 클래스를 사용합니다.
public class TodoItem
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
}
서버 쪽 Web API를 만드는 방법에 대한 지침은 자습서: ASP.NET Core을 사용하여 Web API 만들기를 참조하세요. CORS(원본 간 리소스 공유)에 대한 내용은 이 문서 뒷부분에 나오는 CORS 지침을 참조하세요.
패키지
System.Net.Http.Json에 대한 패키지 참조를 추가합니다.
참고
.NET 앱에 패키지를 추가하는 지침은 패키지 사용 워크플로(NuGet 설명서)에서 패키지 설치 및 관리 아래의 문서를 참조하세요. NuGet.org에서 올바른 패키지 버전을 확인합니다.
HttpClient 서비스 추가
Program.cs에서 앱을 만드는 데 사용된 Blazor 프로젝트 템플릿에 아직 없는 경우 HttpClient 서비스를 추가합니다.
builder.Services.AddScoped(sp =>
new HttpClient
{
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});
HttpClient 및 JSON 도우미
HttpClient는 원본 서버에 대해 다시 요청을 수행하기 위해 미리 구성된 서비스로 사용할 수 있습니다.
HttpClient 및 JSON 도우미(System.Net.Http.Json.HttpClientJsonExtensions)는 타사 웹 API 엔드포인트를 호출하는 데에도 사용됩니다. HttpClient는 브라우저 Fetch API를 사용하여 구현되며 동일한 원본 정책(이 문서 뒷부분에서 설명함) 적용을 비롯한 제한 사항이 적용됩니다.
클라이언트의 기본 주소가 원래 서버의 주소로 설정됩니다. @inject 지시문을 사용하여 HttpClient 인스턴스를 구성 요소에 삽입합니다.
@using System.Net.Http
@inject HttpClient Http
GetFromJsonAsync, PutAsJsonAsync 및 PostAsJsonAsync를 포함하여 HttpClientJsonExtensions에 대한 액세스에 System.Net.Http.Json 네임스페이스를 사용합니다.
@using System.Net.Http.Json
JSON에서 GET(GetFromJsonAsync)
GetFromJsonAsync는 HTTP GET 요청을 보내고 JSON 응답 본문을 구문 분석하여 개체를 만듭니다.
다음 구성 요소 코드에서 todoItems는 구성 요소에 의해 표시됩니다. 구성 요소의 초기화가 완료되면 GetFromJsonAsync가 호출됩니다(OnInitializedAsync).
@using System.Net.Http
@using System.Net.Http.Json
@using System.Threading.Tasks
@inject HttpClient Http
@if (todoItems == null)
{
<p>No Todo Items found.</p>
}
else
{
<ul>
@foreach (var item in todoItems)
{
<li>@item.Name</li>
}
</ul>
}
@code {
private TodoItem[] todoItems;
protected override async Task OnInitializedAsync() =>
todoItems = await Http.GetFromJsonAsync<TodoItem[]>("api/TodoItems");
}
JSON으로 POST(PostAsJsonAsync)
PostAsJsonAsync는 요청 본문에서 JSON으로 직렬화된 값을 포함하는 지정된 URI에 POST 요청을 보냅니다.
다음 구성 요소 코드에서 구성 요소의 바인딩된 요소는 newItemName을 제공합니다. AddItem 메서드는 <button> 요소를 선택하여 트리거됩니다.
@using System.Net.Http
@using System.Net.Http.Json
@using System.Threading.Tasks
@inject HttpClient Http
<input @bind="newItemName" placeholder="New Todo Item" />
<button @onclick="AddItem">Add</button>
@code {
private string newItemName;
private async Task AddItem()
{
var addItem = new TodoItem { Name = newItemName, IsComplete = false };
await Http.PostAsJsonAsync("api/TodoItems", addItem);
}
}
PostAsJsonAsync를 호출하면 HttpResponseMessage가 반환됩니다. 응답 메시지에서 JSON 콘텐츠를 역직렬화하려면 ReadFromJsonAsync 확장 메서드를 사용합니다. 다음 예제에서는 JSON 날씨 데이터를 읽습니다.
var content = await response.Content.ReadFromJsonAsync<WeatherForecast>();
ON으로 JSPUT(PutAsJsonAsync)
PutAsJsonAsync는 JSON 인코딩 콘텐츠와 함께 HTTP PUT 요청을 보냅니다.
다음 구성 요소 코드에서 구성 요소의 바인딩된 요소는 Name 및 IsCompleted에 대한 editItem 값을 제공합니다. 항목의 Id는 UI의 다른 부분에서 해당 항목을 선택하고(표시되지 않음) EditItem을 호출하면 설정됩니다. SaveItem 메서드는 <button> 요소를 선택하여 트리거됩니다.
@using System.Net.Http
@using System.Net.Http.Json
@using System.Threading.Tasks
@inject HttpClient Http
<input type="checkbox" @bind="editItem.IsComplete" />
<input @bind="editItem.Name" />
<button @onclick="SaveItem">Save</button>
@code {
private string id;
private TodoItem editItem = new TodoItem();
private void EditItem(long id)
{
editItem = todoItems.Single(i => i.Id == id);
}
private async Task SaveItem() =>
await Http.PutAsJsonAsync($"api/TodoItems/{editItem.Id}", editItem);
}
PutAsJsonAsync를 호출하면 HttpResponseMessage가 반환됩니다. 응답 메시지에서 JSON 콘텐츠를 역직렬화하려면 ReadFromJsonAsync 확장 메서드를 사용합니다. 다음 예제에서는 JSON 날씨 데이터를 읽습니다.
var content = await response.Content.ReadFromJsonAsync<WeatherForecast>();
추가 확장 메서드
System.Net.Http는 HTTP 요청을 보내고 HTTP 응답을 받기 위한 추가 확장 메서드를 포함합니다. HttpClient.DeleteAsync는 웹 API에 HTTP DELETE 요청을 보내는 데 사용됩니다.
다음 구성 요소 코드에서 <button> 요소는 DeleteItem 메서드를 호출합니다. 바인딩된 <input> 요소는 삭제할 항목의 id를 제공합니다.
@using System.Net.Http
@using System.Threading.Tasks
@inject HttpClient Http
<input @bind="id" />
<button @onclick="DeleteItem">Delete</button>
@code {
private long id;
private async Task DeleteItem() =>
await Http.DeleteAsync($"api/TodoItems/{id}");
}
HttpClient를 IHttpClientFactory로 명명함
IHttpClientFactory 서비스 및 명명된 HttpClient의 구성이 지원됩니다.
앱에 Microsoft.Extensions.Http NuGet 패키지를 추가합니다.
참고
.NET 앱에 패키지를 추가하는 지침은 패키지 사용 워크플로(NuGet 설명서)에서 패키지 설치 및 관리 아래의 문서를 참조하세요. NuGet.org에서 올바른 패키지 버전을 확인합니다.
Program.cs의 경우
builder.Services.AddHttpClient("WebAPI", client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
다음 구성 요소 코드에서:
- IHttpClientFactory의 인스턴스는 명명된 HttpClient를 만듭니다.
- 명명된 HttpClient는 웹 API에서 JSON 날씨 예측 데이터에 대한 GET 요청을 실행하는 데 사용됩니다.
Pages/FetchDataViaFactory.razor:
@page "/fetch-data-via-factory"
@using System.Net.Http
@using System.Net.Http.Json
@using System.Threading.Tasks
@inject IHttpClientFactory ClientFactory
<h1>Fetch data via <code>IHttpClientFactory</code></h1>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<h2>Temperatures by Date</h2>
<ul>
@foreach (var forecast in forecasts)
{
<li>
@forecast.Date.ToShortDateString():
@forecast.TemperatureC ℃
@forecast.TemperatureF ℉
</li>
}
</ul>
}
@code {
private WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
var client = ClientFactory.CreateClient("WebAPI");
forecasts = await client.GetFromJsonAsync<WeatherForecast[]>(
"WeatherForecast");
}
}
형식화된 HttpClient
형식화된 HttpClient는 앱의 기본값 또는 명명된 HttpClient 인스턴스를 사용하여 하나 이상의 웹 API 엔드포인트에서 데이터를 반환합니다.
WeatherForecastClient.cs:
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
public class WeatherForecastHttpClient
{
private readonly HttpClient http;
public WeatherForecastHttpClient(HttpClient http)
{
this.http = http;
}
public async Task<WeatherForecast[]> GetForecastAsync()
{
try
{
return await http.GetFromJsonAsync<WeatherForecast[]>(
"WeatherForecast");
}
catch
{
...
return new WeatherForecast[0];
}
}
}
Program.cs의 경우
builder.Services.AddHttpClient<WeatherForecastHttpClient>(client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
구성 요소는 형식화된 HttpClient를 삽입하여 웹 API를 호출합니다.
다음 구성 요소 코드에서:
- 앞의
WeatherForecastHttpClient인스턴스가 삽입되어 형식화된 HttpClient를 만듭니다. - 형식화된 HttpClient는 웹 API에서 JSON 날씨 예측 데이터에 대한 GET 요청을 실행하는 데 사용됩니다.
Pages/FetchDataViaTypedHttpClient.razor:
@page "/fetch-data-via-typed-httpclient"
@using System.Threading.Tasks
@inject WeatherForecastHttpClient Http
<h1>Fetch data via typed <code>HttpClient</code></h1>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<h2>Temperatures by Date</h2>
<ul>
@foreach (var forecast in forecasts)
{
<li>
@forecast.Date.ToShortDateString():
@forecast.TemperatureC ℃
@forecast.TemperatureF ℉
</li>
}
</ul>
}
@code {
private WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await Http.GetForecastAsync();
}
}
Fetch API 요청 옵션이 있는 HttpClient 및 HttpRequestMessage
HttpClient(API 설명서) 및 HttpRequestMessage를 사용하여 요청을 사용자 지정할 수 있습니다. 예를 들어, HTTP 메서드 및 요청 헤더를 지정할 수 있습니다. 다음 구성 요소는 웹 API 엔드포인트에 대한 POST 요청을 만들고 응답 본문을 표시합니다.
Pages/TodoRequest.razor:
@page "/todo-request"
@using System.Net.Http
@using System.Net.Http.Headers
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject HttpClient Http
@inject IAccessTokenProvider TokenProvider
<h1>ToDo Request</h1>
<button @onclick="PostRequest">Submit POST request</button>
<p>Response body returned by the server:</p>
<p>@responseBody</p>
@code {
private string responseBody;
private async Task PostRequest()
{
var requestMessage = new HttpRequestMessage()
{
Method = new HttpMethod("POST"),
RequestUri = new Uri("https://localhost:10000/api/TodoItems"),
Content =
JsonContent.Create(new TodoItem
{
Name = "My New Todo Item",
IsComplete = false
})
};
var tokenResult = await TokenProvider.RequestAccessToken();
if (tokenResult.TryGetToken(out var token))
{
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token.Value);
requestMessage.Content.Headers.TryAddWithoutValidation(
"x-custom-header", "value");
var response = await Http.SendAsync(requestMessage);
var responseStatusCode = response.StatusCode;
responseBody = await response.Content.ReadAsStringAsync();
}
}
public class TodoItem
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
}
}
Blazor WebAssembly의 HttpClient 구현에서는 Fetch API를 사용합니다. Fetch API를 사용하면 몇 가지 요청 관련 옵션을 구성할 수 있습니다. 옵션은 다음 표에 표시된 HttpRequestMessage 확장 메서드로 구성할 수 있습니다.
| 확장 메서드 | Fetch API 요청 속성 |
|---|---|
| SetBrowserRequestCache | cache |
| SetBrowserRequestCredentials | credentials |
| SetBrowserRequestIntegrity | integrity |
| SetBrowserRequestMode | mode |
일반적인 SetBrowserRequestOption 확장 메서드를 사용하여 추가 옵션을 설정합니다.
HTTP 응답은 일반적으로 응답 콘텐츠에서 동기 읽기를 지원할 수 있도록 버퍼링됩니다. 응답 스트리밍을 지원하도록 설정하려면 요청에서 SetBrowserResponseStreamingEnabled 확장 메서드를 사용합니다.
원본 간 요청에 자격 증명을 포함하려면 SetBrowserRequestCredentials 확장 메서드를 사용합니다.
requestMessage.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
Fetch API 옵션에 대한 자세한 내용은 MDN 웹 문서: WindowOrWorkerGlobalScope.fetch(): 매개 변수를 참조하세요.
웹 API 호출 예제
다음 예제에서는 웹 API를 호출합니다. 이 예제에는 자습서: ASP.NET Core를 사용하여 Web API 만들기 문서에 설명된 샘플 앱을 기준으로 실행 중인 웹 API가 필요합니다. 이 예제는 https://localhost:10000/api/TodoItems에서 웹 API에 요청합니다. 다른 웹 API 주소를 사용하는 경우 구성 요소의 @code 블록에서 ServiceEndpoint 상수 값을 업데이트합니다.
다음 예제는 http://localhost:5000 또는 https://localhost:5001에서 웹 API에 대해 CORS(원본 간 리소스 공유) 요청을 합니다. 웹 API 서비스의 Startup.Configure 메서드에 다음 CORS 미들웨어 구성을 추가합니다.
app.UseCors(policy =>
policy.WithOrigins("http://localhost:5000", "https://localhost:5001")
.AllowAnyMethod()
.WithHeaders(HeaderNames.ContentType));
Blazor 앱에 필요한 경우 WithOrigins의 도메인과 포트를 조정합니다. 자세한 내용은 ASP.NET Core에서 CORS(원본 간 요청) 사용을 참조하세요.
기본적으로 ASP.NET Core 앱은 포트 5000(HTTP) 및 5001(HTTPS)을 사용합니다. 테스트를 위해 두 앱을 동일한 머신에서 동시에 실행하려면 웹 API 앱에 대해 다른 포트(예: 포트 10000)를 사용합니다. 포트 설정에 대한 자세한 내용은 ASP.NET Core의 Kestrel 웹 서버 구현을 참조하세요.
Pages/CallWebAPI.razor:
@page "/call-web-api"
@inject HttpClient Http
<h1>Todo Items</h1>
@if (todoItems == null)
{
<p>No Todo Items found.</p>
}
else
{
<table class="table">
<thead>
<tr>
<th class="text-center">Complete</th>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>
<tr id="editRow" style="display:@editRowStyle">
<td class="text-center">
<input type="checkbox" @bind="editItem.IsComplete" />
</td>
<td>
<input @bind="editItem.Name" />
</td>
<td class="text-center">
<button class="btn btn-success" @onclick="SaveItem">
Save
</button>
<button class="btn btn-danger"
@onclick="@(() => editRowStyle = "none")">
Cancel
</button>
</td>
</tr>
@foreach (var item in todoItems)
{
<tr>
<td class="text-center">
@if (item.IsComplete)
{
<span>✔</span>
}
</td>
<td>@item.Name</td>
<td class="text-center">
<button class="btn btn-warning"
@onclick="@(() => EditItem(item.Id))">Edit</button>
<button class="btn btn-danger"
@onclick="@(async () => await DeleteItem(item.Id))">
Delete
</button>
</td>
</tr>
}
<tr id="addRow">
<td></td>
<td>
<input @bind="newItemName" placeholder="New Todo Item" />
</td>
<td class="text-center">
<button class="btn btn-success" @onclick="@AddItem">Add</button>
</td>
</tr>
</tbody>
</table>
}
@code {
private const string ServiceEndpoint = "https://localhost:10000/api/TodoItems";
private TodoItem[] todoItems;
private TodoItem editItem = new TodoItem();
private string editRowStyle = "none";
private string newItemName;
protected override async Task OnInitializedAsync() => await GetTodoItems();
private async Task GetTodoItems() =>
todoItems = await Http.GetFromJsonAsync<TodoItem[]>(ServiceEndpoint);
private void EditItem(long id)
{
editItem = todoItems.Single(i => i.Id == id);
editRowStyle = "table-row";
}
private async Task AddItem()
{
var addItem = new TodoItem { Name = newItemName, IsComplete = false };
await Http.PostAsJsonAsync(ServiceEndpoint, addItem);
newItemName = string.Empty;
await GetTodoItems();
editRowStyle = "none";
}
private async Task SaveItem()
{
await Http.PutAsJsonAsync($"{ServiceEndpoint}/{editItem.Id}", editItem);
await GetTodoItems();
editRowStyle = "none";
}
private async Task DeleteItem(long id)
{
await Http.DeleteAsync($"{ServiceEndpoint}/{id}");
await GetTodoItems();
editRowStyle = "none";
}
private class TodoItem
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
}
}
오류 처리
개발자 코드에서 웹 API 응답 오류가 발생할 때 처리합니다. 예를 들어 GetFromJsonAsync에는 application/json의 Content-Type을 가진 웹 API의 JSON 응답이 필요합니다. 응답이 JSON 형식이 아닌 경우 콘텐츠 유효성 검사는 NotSupportedException을 throw합니다.
다음 예제에서는 날씨 예측 데이터 요청에 대한 URI 엔드포인트의 철자가 잘못되었습니다. URI는 WeatherForecast에 대한 것이어야 Forecast에서 e 문자가 누락된 WeatherForcast로 호출에 표시됩니다.
GetFromJsonAsync 호출에서는 JSON이 반환되어야 하지만, 웹 API는 text/html의 Content-Type을 사용하여 처리되지 않은 예외에 대한 HTML을 반환합니다. /WeatherForcast에 대한 경로를 찾을 수 없고 미들웨어에서 요청에 대한 페이지 또는 보기를 제공할 수 없기 때문에 처리되지 않은 예외가 발생합니다.
클라이언트의 OnInitializedAsync에서 응답 콘텐츠가 JSON이 아닌 것으로 확인되면 NotSupportedException이 throw됩니다. catch 블록에서 예외가 catch됩니다. 여기서 사용자 지정 논리는 오류를 기록하거나 사용자에게 친숙한 오류 메시지를 표시할 수 있습니다.
Pages/FetchDataReturnsHTMLOnException.razor:
@page "/fetch-data-returns-html-on-exception"
@using System.Net.Http
@using System.Net.Http.Json
@using System.Threading.Tasks
@inject HttpClient Http
<h1>Fetch data but receive HTML on unhandled exception</h1>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<h2>Temperatures by Date</h2>
<ul>
@foreach (var forecast in forecasts)
{
<li>
@forecast.Date.ToShortDateString():
@forecast.TemperatureC ℃
@forecast.TemperatureF ℉
</li>
}
</ul>
}
<p>
@exceptionMessage
</p>
@code {
private WeatherForecast[] forecasts;
private string exceptionMessage;
protected override async Task OnInitializedAsync()
{
try
{
// The URI endpoint "WeatherForecast" is misspelled on purpose on the
// next line. See the preceding text for more information.
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForcast");
}
catch (NotSupportedException exception)
{
exceptionMessage = exception.Message;
}
}
}
참고
앞의 예제는 데모용입니다. 엔드포인트가 없거나 서버에서 처리되지 않은 예외가 발생한 경우에도 JSON을 반환하도록 웹 API를 구성할 수 있습니다.
자세한 내용은 ASP.NET Core Blazor 앱의 오류 처리를 참조하세요.
참고
이 문서는 Web API 호출을 위한 Blazor Server 적용 범위를 로드했습니다. Blazor WebAssembly 적용 범위는 다음과 같은 주제를 해결합니다.
- 웹 API를 호출하여 할 일 목록 항목을 만들고, 읽고, 업데이트하고, 삭제하는 클라이언트 쪽 WebAssembly 앱을 기준으로 하는 Blazor WebAssembly 예제
System.Net.Http.Json패키지HttpClient서비스 구성HttpClient및 JSON 도우미(GetFromJsonAsync,PostAsJsonAsync,PutAsJsonAsync,DeleteAsync)IHttpClientFactory서비스 및 명명된HttpClient의 구성- 형식화된
HttpClient - 요청을 사용자 지정하기 위한
HttpClient및HttpRequestMessage - CORS(원본 간 리소스 공유)를 사용하고 CORS가 Blazor WebAssembly 앱과 관련된 방식을 보여 주는 웹 API 예제
- 개발자 코드에서 Web API 응답 오류를 처리하는 방법
- 웹 API 액세스 테스트를 위한 Blazor 프레임워크 구성 요소 예제
- Web API를 호출하는 Blazor WebAssembly 앱을 개발하기 위한 추가 리소스.
Blazor Server 앱은 일반적으로 IHttpClientFactory를 사용하여 만드는 HttpClient 인스턴스를 사용하여 웹 API를 호출합니다. Blazor Server에 적용되는 지침은 ASP.NET Core에서 IHttpClientFactory를 사용하여 HTTP 요청 만들기를 참조하세요.
Blazor Server 앱은 기본적으로 HttpClient 서비스를 포함하지 않습니다. HttpClient 팩터리 인프라를 사용하여 앱에 HttpClient를 제공합니다.
Startup.cs의 Startup.ConfigureServices에서 다음을 수행합니다.
services.AddHttpClient();
다음 Blazor ServerRazor 구성 요소는 GitHub 분기에 대한 Web API 요청을 ASP.NET Core에서 IHttpClientFactory를 사용하여 HTTP 요청 만들기 문서의 기본 사용법 예제와 유사하게 만듭니다.
Pages/CallWebAPI.razor:
@page "/call-web-api"
@using System.Text.Json
@using System.Text.Json.Serialization;
@inject IHttpClientFactory ClientFactory
<h1>Call web API from a Blazor Server Razor component</h1>
@if (getBranchesError)
{
<p>Unable to get branches from GitHub. Please try again later.</p>
}
else
{
<ul>
@foreach (var branch in branches)
{
<li>@branch.Name</li>
}
</ul>
}
@code {
private IEnumerable<GitHubBranch> branches = Array.Empty<GitHubBranch>();
private bool getBranchesError;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
protected override async Task OnInitializedAsync()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
request.Headers.Add("Accept", "application/vnd.github.v3+json");
request.Headers.Add("User-Agent", "HttpClientFactory-Sample");
var client = ClientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
branches = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(responseStream);
}
else
{
getBranchesError = true;
}
shouldRender = true;
}
public class GitHubBranch
{
[JsonPropertyName("name")]
public string Name { get; set; }
}
}
추가 작업 예제는 ASP.NET Core Blazor 파일 업로드 문서에서 웹 API 컨트롤러에 파일을 업로드하는 Blazor Server 파일 업로드 예제를 참조하세요.
CORS(원본 간 리소스 공유)
브라우저 보안 때문에 웹 페이지에서 해당 웹 페이지를 제공한 도메인이 아닌 다른 도메인에 요청을 수행하는 것이 제한됩니다. 이러한 제한 사항을 동일 원본 정책이라고 합니다. 동일 원본 정책은 악성 사이트에서 다른 사이트의 중요한 데이터를 읽는 것을 제한합니다(차단하지는 않음). 브라우저에서 원본이 다른 엔드포인트로의 요청을 만들려면 엔드포인트가 CORS(원본 간 리소스 공유)를 사용하도록 설정해야 합니다.
Blazor WebAssembly 앱의 CORS 요청에 대한 자세한 내용은 ASP.NET Core Blazor WebAssembly 추가 보안 시나리오를 참조하세요.
CORS에 대한 내용은 ASP.NET Core에서 CORS(원본 간 요청) 사용을 참조하세요. 문서의 예제는 Blazor WebAssembly 앱에 직접 관련되지 않지만 일반적인 CORS 개념을 배우는 데 유용합니다.
자세한 내용은 ASP.NET Core에서 CORS(원본 간 요청) 사용을 참조하세요.
웹 API 액세스 테스트를 위한 Blazor 프레임워크 구성 요소 예제
Firefox Browser Developer 및 Postman과 같은 다양한 네트워크 도구를 웹 API 백 엔드 앱을 테스트하는 데 공개적으로 사용할 수 있습니다. Blazor 프레임워크의 참조 원본에는 테스트에 유용한 HttpClient 테스트 자산이 포함됩니다.
dotnet/aspnetcore GitHub 리포지토리의 HttpClientTest 자산
참고
.NET 참조 원본의 설명서 링크는 일반적으로 다음 릴리스의 .NET을 위한 현재 개발을 나타내는 리포지토리의 기본 분기를 로드합니다. 특정 릴리스를 위한 태그를 선택하려면 Switch branches or tags(분기 또는 태그 전환) 드롭다운 목록을 사용합니다. 자세한 내용은 ASP.NET Core 소스 코드(dotnet/AspNetCore.Docs #26205)의 버전 태그를 선택하는 방법을 참조하세요.
추가 자료
- ASP.NET Core Blazor WebAssembly 추가 보안 시나리오: 보안 웹 API 요청을 만드는 데 HttpClient를 사용하는 방법에 대한 설명을 포함합니다.
- ASP.NET Core에서 CORS(원본 간 요청) 사용: 콘텐츠가 Blazor WebAssembly 앱이 아닌 ASP.NET Core 앱에 적용되기는 하지만, 이 문서에서는 일반적인 CORS 개념을 설명합니다.
- W3C의 CORS(원본 간 리소스 공유)
- Fetch API
- ASP.NET Core Blazor Server 추가 보안 시나리오: 보안 웹 API 요청을 만드는 데 HttpClient를 사용하는 방법에 대한 설명을 포함합니다.
- ASP.NET Core에서 IHttpClientFactory를 사용하여 HTTP 요청 만들기
- ASP.NET Core에서 HTTPS 적용
- ASP.NET Core에서 CORS(원본 간 요청) 사용
- Kestrel HTTPS 엔드포인트 구성
- W3C의 CORS(원본 간 리소스 공유)