[C#/ASP.NET MVC/.NETCORE] HttpClientFactoryServiceCollectionExtensions 클래스 : AddHttpClient 확장 메소드를 사용해 HttpClient 관련 객체 의존성 주입 사용하기
C#/ASP.NET MVC 2020. 11. 7. 22:25728x90
728x170
▶ TodoClient.cs
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using TestProject.Models;
namespace TestProject
{
/// <summary>
/// 할일 클라이언트
/// </summary>
public class TodoClient
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// JSON 직렬화기 옵션
/// </summary>
private static readonly JsonSerializerOptions _jsonSerializerOptions = new JsonSerializerOptions
{
IgnoreNullValues = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// HTTP 클라이언트
/// </summary>
private readonly HttpClient httpClient;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - TodoClient(httpClient)
/// <summary>
/// 생성자
/// </summary>
/// <param name="httpClient">HTTP 클라이언트</param>
public TodoClient(HttpClient httpClient)
{
this.httpClient = httpClient;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Metohd
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 리스트 구하기 (비동기) - GetListAsync()
/// <summary>
/// 리스트 구하기 (비동기)
/// </summary>
/// <returns>할일 리스트 태스크</returns>
public async Task<List<TodoModel>> GetListAsync()
{
using HttpResponseMessage httpResponseMessage = await this.httpClient.GetAsync("/api/todo");
httpResponseMessage.EnsureSuccessStatusCode();
using Stream stream = await httpResponseMessage.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync<List<TodoModel>>(stream, _jsonSerializerOptions);
}
#endregion
#region 항목 구하기 (비동기) - GetItemAsync(id)
/// <summary>
/// 항목 구하기 (비동기)
/// </summary>
/// <param name="id">ID</param>
/// <returns>할일 태스크</returns>
public async Task<TodoModel> GetItemAsync(long id)
{
using HttpResponseMessage httpResponseMessage = await this.httpClient.GetAsync($"/api/todo/{id}");
if(httpResponseMessage.StatusCode == HttpStatusCode.NotFound)
{
return null;
}
httpResponseMessage.EnsureSuccessStatusCode();
using Stream stream = await httpResponseMessage.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync<TodoModel>(stream, _jsonSerializerOptions);
}
#endregion
#region 항목 생성하기 (비동기) - CreateItemAsync(todo)
/// <summary>
/// 항목 생성하기 (비동기)
/// </summary>
/// <param name="todo">할일</param>
/// <returns>태스크</returns>
public async Task CreateItemAsync(TodoModel todo)
{
StringContent stringContent = new StringContent
(
JsonSerializer.Serialize(todo, _jsonSerializerOptions),
Encoding.UTF8,
"application/json"
);
using HttpResponseMessage httpResponseMessage = await this.httpClient.PostAsync
(
"/api/todo",
stringContent
);
httpResponseMessage.EnsureSuccessStatusCode();
}
#endregion
#region 항목 저장하기 (비동기) - SaveItemAsync(todo)
/// <summary>
/// 항목 저장하기 (비동기)
/// </summary>
/// <param name="todo">할일</param>
/// <returns>태스크</returns>
public async Task SaveItemAsync(TodoModel todo)
{
StringContent stringContent = new StringContent
(
JsonSerializer.Serialize(todo),
Encoding.UTF8,
"application/json"
);
using HttpResponseMessage httpResponseMessage = await this.httpClient.PutAsync
(
$"/api/todo/{todo.ID}",
stringContent
);
httpResponseMessage.EnsureSuccessStatusCode();
}
#endregion
#region 항목 삭제하기 (비동기) - DeleteItemAsync(id)
/// <summary>
/// 항목 삭제하기 (비동기)
/// </summary>
/// <param name="id">ID</param>
/// <returns>태스크</returns>
public async Task DeleteItemAsync(long id)
{
using HttpResponseMessage httpResponseMessage = await this.httpClient.DeleteAsync($"/api/todo/{id}");
httpResponseMessage.EnsureSuccessStatusCode();
}
#endregion
}
}
728x90
▶ Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using TestProject.Data;
namespace TestProject
{
/// <summary>
/// 시작
/// </summary>
public class Startup
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 서비스 컬렉션 구성하기 - ConfigureServices(services)
/// <summary>
/// 서비스 컬렉션 구성하기
/// </summary>
/// <param name="services">서비스 컬렉션</param>
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DatabaseContext>
(
options => options.UseInMemoryDatabase("TestDB")
);
services.AddHttpContextAccessor();
services.AddHttpClient<TodoClient>
(
(serviceProvider, httpClient) =>
{
HttpRequest httpRequest = serviceProvider.GetRequiredService<IHttpContextAccessor>().HttpContext.Request;
httpClient.BaseAddress = new Uri(UriHelper.BuildAbsolute(httpRequest.Scheme, httpRequest.Host, httpRequest.PathBase));
httpClient.Timeout = TimeSpan.FromSeconds(5);
}
);
services.AddControllers();
services.AddRazorPages();
}
#endregion
#region 구성하기 - Configure(app, environment)
/// <summary>
/// 구성하기
/// </summary>
/// <param name="app">애플리케이션 빌더</param>
/// <param name="environment">웹 호스트 환경</param>
public void Configure(IApplicationBuilder app, IWebHostEnvironment environment)
{
if(environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints
(
endpoints =>
{
endpoints.MapControllers();
endpoints.MapRazorPages();
}
);
}
#endregion
}
}
300x250
▶ Pages/Index.cshtml
@page
@using TestProject.Models
@model IndexModel
@{
ViewData["Title"] = "Home";
}
<div class="row justify-content-center">
<div class="col-md-6">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<form asp-page-handler="Create" method="post">
<div class="input-group input-group-sm">
<input name="name"
class="form-control form-control-sm"
placeholder="Something I need to do">
<div class="input-group-append">
<button class="btn btn-primary">Add to List</button>
</div>
</div>
</form>
</div>
</div>
@if(Model.IncompleteTodoList.Any())
{
<hr>
<div class="row justify-content-center">
<div class="col-md-6">
<ul class="list-group mb-2">
@foreach(TodoModel todo in Model.IncompleteTodoList)
{
<a asp-page="/Item" asp-route-id="@todo.ID"
class="list-group-item list-group-item-action">
@todo.Name
</a>
}
</ul>
</div>
</div>
}
@if(Model.CompleteTodoList.Any())
{
<div class="row justify-content-center">
<div class="col-md-6">
<p class="text-center">
<small>@Model.CompleteTodoList.Count completed</small>
</p>
<ul class="list-group">
@foreach(TodoModel todo in Model.CompleteTodoList)
{
<a asp-page="/Item" asp-route-id="@todo.ID"
class="list-group-item list-group-item-action text-muted">
@todo.Name
</a>
}
</ul>
</div>
</div>
}
@section Styles {
<style>.list-group-item.text-muted{text-decoration:line-through}</style>
}
▶ Pages/Index.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using TestProject.Models;
namespace TestProject.Pages
{
/// <summary>
/// 인덱스 모델
/// </summary>
public class IndexModel : PageModel
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 할일 클라이언트
/// </summary>
private readonly TodoClient client;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 완료 할일 리스트 - CompleteTodoList
/// <summary>
/// 완료 할일 리스트
/// </summary>
public List<TodoModel> CompleteTodoList { get; private set; }
#endregion
#region 미완료 할일 리스트 - IncompleteTodoList
/// <summary>
/// 미완료 할일 리스트
/// </summary>
public List<TodoModel> IncompleteTodoList { get; private set; }
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - IndexModel(client)
/// <summary>
/// 생성자
/// </summary>
/// <param name="client">할일 클라이언트</param>
public IndexModel(TodoClient client)
{
this.client = client;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region GET 요청시 처리하기 (비동기) - OnGetAsync()
/// <summary>
/// GET 요청시 처리하기 (비동기)
/// </summary>
/// <returns>태스크</returns>
public async Task OnGetAsync()
{
List<TodoModel> todoList = await this.client.GetListAsync();
CompleteTodoList = todoList.Where(x => x.IsComplete).ToList();
IncompleteTodoList = todoList.Except(CompleteTodoList).ToList();
}
#endregion
#region POST 생성 요청시 처리하기 (비동기) - OnPostCreateAsync(name)
/// <summary>
/// POST 생성 요청시 처리하기 (비동기)
/// </summary>
/// <param name="name">명칭</param>
/// <returns>액션 결과 태스크</returns>
public async Task<IActionResult> OnPostCreateAsync([Required] string name)
{
if(!ModelState.IsValid)
{
return RedirectToPage();
}
TodoModel newTodo = new TodoModel
{
Name = name
};
await this.client.CreateItemAsync(newTodo);
return RedirectToPage();
}
#endregion
}
}
▶ Pages/Item.cshtml
@page "/Item/{id}"
@model ItemModel
@{
ViewData["Title"] = "Item";
}
<div class="row justify-content-center">
<div class="col-md-6">
<form asp-page-handler="Save" method="POST">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="TodoInput.Name"></label>
<input asp-for="TodoInput.Name" class="form-control">
<span asp-validation-for="TodoInput.Name" class="text-danger"></span>
</div>
<div class="form-group form-check">
<input asp-for="TodoInput.IsComplete" class="form-check-input">
<label asp-for="TodoInput.IsComplete" class="form-check-label">Completed</label>
</div>
<div class="form-group">
<button class="btn btn-primary">Save</button>
<button asp-page-handler="Delete" class="btn btn-danger">Delete</button>
</div>
</form>
<div>
<a asp-page="/Index">Back to List</a>
</div>
</div>
</div>
▶ Pages/Item.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Threading.Tasks;
using TestProject.Models;
namespace TestProject.Pages
{
/// <summary>
/// 항목 모델
/// </summary>
public class ItemModel : PageModel
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 할일 클라이언트
/// </summary>
private readonly TodoClient client;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 할일 입력 - TodoInput
/// <summary>
/// 할일 입력
/// </summary>
[BindProperty]
public TodoInputModel TodoInput { get; set; }
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - ItemModel(client)
/// <summary>
/// 생성자
/// </summary>
/// <param name="client">할일 클라이언트</param>
public ItemModel(TodoClient client)
{
this.client = client;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region GET 요청시 처리하기 (비동기) - OnGetAsync(id)
/// <summary>
/// GET 요청시 처리하기 (비동기)
/// </summary>
/// <param name="id">ID</param>
/// <returns>액션 결과 태스크</returns>
public async Task<IActionResult> OnGetAsync(int id)
{
TodoModel todo = await this.client.GetItemAsync(id);
if(todo == null)
{
return RedirectToPage("/Index");
}
TodoInput = new TodoInputModel
{
Name = todo.Name,
IsComplete = todo.IsComplete
};
return Page();
}
#endregion
#region POST 저장 요청시 처리하기 (비동기) - OnPostSaveAsync(id)
/// <summary>
/// POST 저장 요청시 처리하기 (비동기)
/// </summary>
/// <param name="id">ID</param>
/// <returns>액션 결과 태스크</returns>
public async Task<IActionResult> OnPostSaveAsync(int id)
{
if(!ModelState.IsValid)
{
return Page();
}
TodoModel updateTodo = new TodoModel
{
ID = id,
Name = TodoInput.Name,
IsComplete = TodoInput.IsComplete
};
await this.client.SaveItemAsync(updateTodo);
return RedirectToPage("/Index");
}
#endregion
#region POST 삭제 요청시 처리하기 (비동기) - OnPostDeleteAsync(id)
/// <summary>
/// POST 삭제 요청시 처리하기 (비동기)
/// </summary>
/// <param name="id">ID</param>
/// <returns>액션 결과 태스크</returns>
public async Task<IActionResult> OnPostDeleteAsync(int id)
{
await this.client.DeleteItemAsync(id);
return RedirectToPage("/Index");
}
#endregion
}
}
728x90
그리드형(광고전용)