자습서: ASP.NET Core를 사용하여 웹 API 만들기

작성자: Rick Anderson, Kirk Larkin, Mike Wasson

이 자습서에서는 ASP.NET Core를 사용하여 웹 API를 빌드하는 작업의 기본 사항을 설명합니다.

이 자습서에서는 다음과 같은 작업을 수행하는 방법을 살펴봅니다.

  • 웹 API 프로젝트를 만듭니다.
  • 모델 클래스와 데이터베이스 컨텍스트를 추가합니다.
  • CRUD 메서드로 컨트롤러를 스캐폴드합니다.
  • 라우팅, URL 경로 및 반환 값을 구성합니다.
  • Postman을 사용하여 웹 API를 호출합니다.

과정을 마치면 웹 API를 통해 데이터베이스에 저장된 “할 일” 항목을 관리할 수 있습니다.

개요

이 자습서에서는 다음 API를 만듭니다.

API 설명 요청 본문 응답 본문
GET /api/todoitems 할 일 항목 모두 가져오기 없음 할 일 항목의 배열
GET /api/todoitems/{id} ID로 항목 가져오기 없음 할 일 항목
POST /api/todoitems 새 항목 추가 할 일 항목 할 일 항목
PUT /api/todoitems/{id} 기존 항목 업데이트   할 일 항목 없음
DELETE /api/todoitems/{id}     항목 삭제     없음 없음

다음 다이어그램에서는 앱의 디자인을 보여줍니다.

클라이언트는 왼쪽에 상자로 표시됩니다. 클라이언트는 요청을 제출하고 오른쪽에 그려진 상자인 애플리케이션에서 응답을 받습니다. 애플리케이션 상자 내에서 3개의 상자는 컨트롤러, 모델 및 데이터 액세스 계층을 나타냅니다. 요청은 애플리케이션의 컨트롤러로 들어오고 읽기/쓰기 작업은 컨트롤러와 데이터 액세스 계층 간에 발생합니다. 모델은 직렬화되며 응답에서 클라이언트에 반환됩니다.

사전 요구 사항

웹 프로젝트 만들기

  • 파일 메뉴 에서 새로 만들기 > 프로젝트 를 선택합니다.
  • ASP.NET Core Web API 템플릿을 선택하고 다음 을 클릭합니다.
  • 프로젝트 이름을 TodoApi 로 지정하고 만들기 를 클릭합니다.
  • 새 ASP.NET Core 웹 애플리케이션 만들기 대화 상자에서 .NET CoreASP.NET Core 5.0 이 선택되었는지 확인합니다. API 템플릿을 선택하고 만들기 를 클릭합니다.

VS 새 프로젝트 대화 상자

프로젝트 테스트

프로젝트 템플릿은 Swagger를 지원하는 WeatherForecast API를 만듭니다.

Ctrl+F5를 눌러 디버거 없이 실행합니다.

프로젝트가 SSL을 사용하도록 아직 구성되지 않은 경우 Visual Studio에 다음 대화 상자가 표시됩니다.

이 프로젝트는 SSL을 사용하도록 구성되었습니다. 브라우저에서 SSL 경고를 방지하기 위해 IIS Express가 생성한 자체 서명된 인증서를 신뢰하도록 선택할 수 있습니다. IIS Express SSL 인증서를 신뢰하시겠습니까?

IIS Express SSL 인증서를 신뢰하는 경우 를 선택합니다.

다음 대화 상자가 표시됩니다.

보안 경고 대화 상자

개발 인증서를 신뢰하는 데 동의하는 경우 를 선택합니다.

Firefox 브라우저를 신뢰하는 방법에 대한 자세한 내용은 Firefox SEC_ERROR_INADEQUATE_KEY_USAGE 인증서 오류를 참조하세요.

Visual Studio에서 다음을 시작합니다.

  • IIS Express 웹 서버.
  • 기본 브라우저를 시작하고 https://localhost:<port>/swagger/index.html로 이동합니다. 여기서 <port>는 임의로 선택한 포트 번호입니다.

Swagger 페이지 /swagger/index.html이 표시됩니다. GET > 사용해 보기 > 실행 을 선택합니다. 페이지에 다음이 표시됩니다.

  • WeatherForecast API를 테스트할 Curl 명령
  • WeatherForecast API를 테스트할 URL
  • 응답 코드, 본문 및 헤더
  • 미디어 유형과 예제 값 및 스키마가 포함된 드롭다운 목록 상자

Swagger 페이지가 표시되지 않는 경우 이 GitHub 이슈를 참조하세요.

Swagger는 웹 API에 유용한 설명서 및 도움말 페이지를 생성하는 데 사용됩니다. 이 자습서에서는 웹 API 만들기에 대해 집중적으로 다룹니다. Swagger에 대한 자세한 내용은 Swagger/OpenAPI를 사용한 ASP.NET Core 웹 API 설명서를 참조하세요.

브라우저에서 요청 URL https://localhost:<port>/WeatherForecast를 복사하여 붙여넣습니다.

다음과 비슷한 JSON이 반환됩니다.

[
    {
        "date": "2019-07-16T19:04:05.7257911-06:00",
        "temperatureC": 52,
        "temperatureF": 125,
        "summary": "Mild"
    },
    {
        "date": "2019-07-17T19:04:05.7258461-06:00",
        "temperatureC": 36,
        "temperatureF": 96,
        "summary": "Warm"
    },
    {
        "date": "2019-07-18T19:04:05.7258467-06:00",
        "temperatureC": 39,
        "temperatureF": 102,
        "summary": "Cool"
    },
    {
        "date": "2019-07-19T19:04:05.7258471-06:00",
        "temperatureC": 10,
        "temperatureF": 49,
        "summary": "Bracing"
    },
    {
        "date": "2019-07-20T19:04:05.7258474-06:00",
        "temperatureC": -1,
        "temperatureF": 31,
        "summary": "Chilly"
    }
]

launchUrl 업데이트

Properties\launchSettings.json 에서 launchUrl"swagger"에서 "api/todoitems"로 업데이트합니다.

"launchUrl": "api/todoitems",

Swagger가 제거되므로 위 태그는 다음 섹션에 추가된 컨트롤러의 GET 메서드로 시작되는 URL을 변경합니다.

모델 클래스 추가

모델 은 앱에서 관리하는 데이터를 나타내는 일련의 클래스입니다. 이 앱에 대한 모델은 단일 TodoItem 클래스입니다.

  • 솔루션 탐색기 에서 프로젝트를 마우스 오른쪽 단추로 클릭합니다. 추가 > 새 폴더 를 선택합니다. 폴더 이름을 Models 로 지정합니다.

  • Models 폴더를 마우스 오른쪽 단추로 클릭하고 추가 > 클래스 를 선택합니다. 클래스 이름을 TodoItem 으로 지정하고 추가 를 선택합니다.

  • 템플릿 코드를 다음으로 바꿉니다.

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public bool IsComplete { get; set; }
    }
}

Id 속성은 관계형 데이터베이스에서 고유 키로 작동합니다.

모델 클래스는 프로젝트의 어디로든 이동할 수 있지만 규칙에 따라 Models 폴더를 사용합니다.

데이터베이스 컨텍스트 추가

데이터베이스 컨텍스트 는 데이터 모델에 맞게 Entity Framework 기능을 조정하는 주 클래스입니다. Microsoft.EntityFrameworkCore.DbContext 클래스에서 파생시키는 방식으로 이 클래스를 만듭니다.

NuGet 패키지 추가

  • 도구 메뉴에서 NuGet 패키지 관리자 > 솔루션용 NuGet 패키지 관리 를 선택합니다.
  • 찾아보기 탭을 선택한 다음, 검색 상자에 Microsoft.EntityFrameworkCore.InMemory를 입력합니다.
  • 왼쪽 창에서 Microsoft.EntityFrameworkCore.InMemory를 선택합니다.
  • 오른쪽 창에서 프로젝트 확인란을 선택하고 설치 를 선택합니다.

NuGet 패키지 관리자

TodoContext 데이터베이스 컨텍스트 추가

  • Models 폴더를 마우스 오른쪽 단추로 클릭하고 추가 > 클래스 를 선택합니다. 클래스 이름을 TodoContext 로 지정하고 추가 를 클릭합니다.
  • 다음 코드를 입력합니다.

    using Microsoft.EntityFrameworkCore;
    
    namespace TodoApi.Models
    {
        public class TodoContext : DbContext
        {
            public TodoContext(DbContextOptions<TodoContext> options)
                : base(options)
            {
            }
    
            public DbSet<TodoItem> TodoItems { get; set; }
        }
    }
    

데이터베이스 컨텍스트 등록

ASP.NET Core에서는 DB 컨텍스트와 같은 서비스를 DI(종속성 주입) 컨테이너에 등록해야 합니다. 컨테이너는 컨트롤러에 서비스를 제공합니다.

다음 코드로 Startup.cs 를 업데이트합니다.

// Unused usings removed
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddDbContext<TodoContext>(opt =>
                                               opt.UseInMemoryDatabase("TodoList"));
            //services.AddSwaggerGen(c =>
            //{
            //    c.SwaggerDoc("v1", new OpenApiInfo { Title = "TodoApi", Version = "v1" });
            //});
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                //app.UseSwagger();
                //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TodoApi v1"));
            }

            app.UseHttpsRedirection();
            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

위의 코드는

  • Swagger 호출을 제거합니다.
  • 사용되지 않는 using 선언을 제거합니다.
  • DI 컨테이너에 데이터베이스 컨텍스트를 추가합니다.
  • 데이터베이스 컨텍스트가 메모리 내 데이터베이스를 사용하도록 지정합니다.

컨트롤러 스캐폴드

  • Controllers 폴더를 마우스 오른쪽 단추로 클릭합니다.

  • 추가 > 스캐폴드 항목 새로 만들기 를 선택합니다.

  • Entity Framework를 사용하며 동작이 포함된 API 컨트롤러 를 선택하고 추가 를 선택합니다.

  • Entity Framework를 사용하며 동작이 포함된 API 컨트롤러 추가 대화 상자에서:

    • 모델 클래스 에서 TodoItem(TodoApi.Models) 을 선택합니다.
    • 데이터 컨텍스트 클래스 에서 TodoContext(TodoApi.Models) 를 선택합니다.
    • 추가 를 선택합니다.

생성된 코드는:

  • [ApiController] 특성으로 클래스를 표시합니다. 이 특성은 컨트롤러가 웹 API 요청에 응답함을 나타냅니다. 특성을 사용하도록 설정하는 특정 동작에 대한 정보는 ASP.NET Core로 Web API 만들기를 참조하세요.
  • DI를 사용하여 데이터베이스 컨텍스트(TodoContext)를 컨트롤러에 삽입합니다. 컨트롤러의 각 CRUD 메서드에서 해당 데이터베이스 컨텍스트를 사용합니다.

ASP.NET Core 템플릿과 관련해서 다음 사항을 확인합니다.

  • 뷰가 있는 컨트롤러용 ASP.NET Core 템플릿의 경우, 경로 템플릿에 [action]이 포함됩니다.
  • API 컨트롤러용 ASP.NET Core 템플릿의 경우, 경로 템플릿에 [action]이 포함되지 않습니다.

[action] 토큰이 경로 템플릿에 없는 경우 경로에서 작업 이름이 제외됩니다. 즉, 일치하는 경로에서 작업과 연결된 메서드 이름이 사용되지 않습니다.

PostTodoItem 만들기 메서드 업데이트

nameof 연산자를 사용하도록 PostTodoItem의 return 문을 업데이트합니다.

// POST: api/TodoItems
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    //return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}

위의 코드는 [HttpPost] 특성으로 표시되는 HTTP POST 메서드입니다. 이 메서드는 HTTP 요청 본문에서 할 일 항목 값을 가져옵니다.

자세한 내용은 Http[동사] 특성을 사용한 특성 라우팅을 참조하세요.

CreatedAtAction 메서드는 다음 작업을 수행합니다.

  • 성공하면 HTTP 201 상태 코드를 반환합니다. HTTP 201은 서버에서 새 리소스를 만드는 HTTP POST 메서드의 표준 응답입니다.
  • 응답에 대한 위치 헤더를 추가합니다. Location 헤더는 새로 만들어진 할 일 항목의 URI를 지정합니다. 자세한 내용은 10.2.2 201 생성됨을 참조하세요.
  • Location 헤더의 URI를 만들려면 GetTodoItem 작업을 참조합니다. C# nameof 키워드는 CreatedAtAction 호출에서 작업 이름의 하드 코딩을 방지하는 데 사용됩니다.

Postman을 설치합니다.

이 자습서에서는 Postman을 사용하여 웹 API를 테스트합니다.

  • Postman을 설치합니다.
  • 웹앱을 시작합니다.
  • Postman을 시작합니다.
  • SSL 인증서 확인 을 사용하지 않도록 설정합니다.
    • 파일 > 설정(일반 탭)에서 SSL 인증서 확인 을 사용하지 않도록 설정합니다.

      경고

      컨트롤러를 테스트한 후에 SSL 인증서 확인을 다시 사용하도록 설정합니다.

Postman을 사용하여 PostTodoItem 테스트

  • 새 요청을 만듭니다.

  • HTTP 메서드를 POST로 설정합니다.

  • URI를 https://localhost:<port>/api/todoitems로 설정합니다. 예: https://localhost:5001/api/todoitems.

  • 본문 탭을 선택합니다.

  • 원시 라디오 단추를 선택합니다.

  • 유형을 JSON(application/json) 으로 설정합니다.

  • 요청 본문에서 할 일 항목에 대한 JSON을 입력합니다.

    {
      "name":"walk dog",
      "isComplete":true
    }
    
  • 보내기 를 선택합니다.

    생성 요청이 있는 Postman

위치 헤더 URI 테스트

위치 헤더 URI는 브라우저에서 테스트할 수 있습니다. 위치 헤더 URI를 복사하여 브라우저에 붙여넣습니다.

Postman에서 테스트하려면 다음을 수행합니다.

  • 응답 창에서 헤더 탭을 선택합니다.

  • 위치 헤더 값을 복사합니다.

    Postman 콘솔의 헤더 탭

  • HTTP 메서드를 GET로 설정합니다.

  • URI를 https://localhost:<port>/api/todoitems/1로 설정합니다. 예: https://localhost:5001/api/todoitems/1.

  • 보내기 를 선택합니다.

GET 메서드 검사

두 개의 GET 엔드포인트가 구현됩니다.

  • GET /api/todoitems
  • GET /api/todoitems/{id}

브라우저 또는 Postman에서 두 개의 엔드포인트를 호출하여 앱을 테스트합니다. 예를 들어:

  • https://localhost:5001/api/todoitems
  • https://localhost:5001/api/todoitems/1

GetTodoItems를 호출하면 다음과 비슷한 응답이 생성됩니다.

[
  {
    "id": 1,
    "name": "Item1",
    "isComplete": false
  }
]

Postman을 사용하여 Get 테스트

  • 새 요청을 만듭니다.
  • HTTP 메서드를 GET 으로 설정합니다.
  • 요청 URI를 https://localhost:<port>/api/todoitems로 설정합니다. 예: https://localhost:5001/api/todoitems.
  • Postman에서 두 개의 창 보기 를 설정합니다.
  • 보내기 를 선택합니다.

이 앱은 메모리 내 데이터베이스를 사용합니다. 앱이 중지된 후 시작되면 이전 GET 요청이 데이터를 반환하지 않습니다. 데이터가 반환되지 않으면 앱에 데이터를 POST합니다.

라우팅 및 URL 경로

[HttpGet] 특성은 HTTP GET 요청에 응답하는 메서드를 나타냅니다. 각 방법에 대한 URL 경로는 다음과 같이 구성됩니다.

  • 컨트롤러의 Route 특성에서 템플릿 문자열로 시작합니다.

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    {
        private readonly TodoContext _context;
    
        public TodoItemsController(TodoContext context)
        {
            _context = context;
        }
    
  • [controller]를 컨트롤러의 이름으로 바꿉니다. 일반적으로 컨트롤러 클래스 이름에서 "Controller" 접미사를 뺀 이름입니다. 이 샘플의 경우 컨트롤러 클래스 이름은 TodoItems Controller이므로 컨트롤러 이름은 “TodoItems”입니다. ASP.NET Core 라우팅은 대/소문자를 구분하지 않습니다.

  • [HttpGet] 특성에 경로 템플릿(예: [HttpGet("products")])이 있는 경우 경로에 추가합니다. 이 샘플은 템플릿을 사용하지 않습니다. 자세한 내용은 Http[동사] 특성을 사용한 특성 라우팅을 참조하세요.

다음 GetTodoItem 메서드에서 "{id}"는 할 일 항목의 고유 식별자에 대한 자리 표시자 변수입니다. GetTodoItem이 호출되면 URL의 "{id}" 값을 id 매개 변수의 메서드에 제공합니다.

// GET: api/TodoItems/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

반환 값

GetTodoItemsGetTodoItem 메서드의 반환 형식은 ActionResult<T> 형식입니다. ASP.NET Core는 자동으로 JSON에 개체를 직렬화하고 JSON을 응답 메시지의 본문에 기록합니다. 이 반환 형식의 응답 코드는 200 OK이며 처리되지 않은 예외가 없다고 가정합니다. 처리되지 않은 예외는 5xx 오류로 변환됩니다.

ActionResult 반환 형식은 다양한 HTTP 상태 코드를 나타낼 수 있습니다. 예를 들어 GetTodoItem은 두 가지 상태 값을 반환할 수 있습니다.

  • 요청된 ID와 일치하는 항목이 없는 경우 메서드에서 404 상태 NotFound 오류 코드를 반환합니다.
  • 그렇지 않으면 메서드가 JSON 응답 본문에서 200을 반환합니다. item을 반환하면 HTTP 200 응답이 발생합니다.

PutTodoItem 메서드

다음과 같이 PutTodoItem 메서드를 검사합니다.

// PUT: api/TodoItems/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
    if (id != todoItem.Id)
    {
        return BadRequest();
    }

    _context.Entry(todoItem).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!TodoItemExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}

HTTP PUT을 사용하는 것을 제외하고 PutTodoItemPostTodoItem와 비슷합니다. 응답은 204(콘텐츠 없음)입니다. HTTP 사양에 따라 PUT 요청의 경우 클라이언트는 변경 내용만이 아니라 전체 업데이트된 엔터티를 보내야 합니다. 부분 업데이트를 지원하려면 HTTP PATCH를 사용합니다.

PutTodoItem을 호출하는 중 오류가 발생하면 GET을 호출하여 데이터베이스에 항목이 있는지 확인합니다.

PutTodoItem 메서드 테스트

이 샘플은 앱이 시작될 때마다 초기화되어야 하는 메모리 내 데이터베이스를 사용합니다. PUT 호출을 실행하기 전에 데이터베이스에 항목이 있어야 합니다. GET을 호출하여 PUT 호출을 실행하기 전에 데이터베이스에 항목이 있는지 확인합니다.

Id = 1인 할 일 항목을 업데이트하고 해당 이름을 "feed fish"로 설정합니다.

  {
    "Id":1,
    "name":"feed fish",
    "isComplete":true
  }

다음 이미지는 Postman 업데이트를 보여줍니다.

204(콘텐츠 없음) 응답을 보여주는 Postman 콘솔

DeleteTodoItem 메서드

다음과 같이 DeleteTodoItem 메서드를 검사합니다.

// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return NoContent();
}

DeleteTodoItem 메서드 테스트

Postman을 사용하여 할 일 항목을 삭제합니다.

  • 메서드를 DELETE로 설정합니다.
  • 예를 들어 삭제할 개체의 URI를 https://localhost:5001/api/todoitems/1로 설정합니다.
  • 보내기 를 선택합니다.

과도한 게시 방지

현재 샘플 앱은 전체 TodoItem 개체를 공개합니다. 일반적으로 프로덕션 앱은 모델의 하위 집합을 사용하여 입력 및 반환되는 데이터를 제한합니다. 이 동작에는 여러 가지 이유가 있으며, 보안이 주요 이유 중 하나입니다. 일반적으로 모델의 하위 집합을 DTO(데이터 전송 개체), 입력 모델 또는 뷰 모델이라고 합니다. 이 문서에서는 DTO 를 사용합니다.

DTO는 다음과 같은 용도로 사용할 수 있습니다.

  • 과도한 게시를 방지합니다.
  • 클라이언트에 표시되지 않아야 하는 속성을 숨깁니다.
  • 페이로드 크기를 줄이기 위해 일부 속성을 생략합니다.
  • 중첩된 개체를 포함하는 개체 그래프를 평면화합니다. 클라이언트에는 평면화된 개체 그래프가 더 편리할 수 있습니다.

DTO 방법을 설명하려면 비밀 필드를 포함하도록 TodoItem 클래스를 업데이트합니다.

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public bool IsComplete { get; set; }
        public string Secret { get; set; }
    }
}

이 앱에서는 숨겨진 필드를 숨겨야 하지만, 관리 앱은 숨겨진 필드를 공개할 수 있습니다.

비밀 필드를 게시하고 가져올 수 있는지 확인합니다.

DTO 모델을 만듭니다.

public class TodoItemDTO
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
}

TodoItemDTO를 사용하도록 TodoItemsController를 업데이트합니다.

// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
{
    return await _context.TodoItems
        .Select(x => ItemToDTO(x))
        .ToListAsync();
}

[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return ItemToDTO(todoItem);
}

[HttpPut("{id}")]
public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
{
    if (id != todoItemDTO.Id)
    {
        return BadRequest();
    }

    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    todoItem.Name = todoItemDTO.Name;
    todoItem.IsComplete = todoItemDTO.IsComplete;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
    {
        return NotFound();
    }

    return NoContent();
}

[HttpPost]
public async Task<ActionResult<TodoItemDTO>> CreateTodoItem(TodoItemDTO todoItemDTO)
{
    var todoItem = new TodoItem
    {
        IsComplete = todoItemDTO.IsComplete,
        Name = todoItemDTO.Name
    };

    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    return CreatedAtAction(
        nameof(GetTodoItem),
        new { id = todoItem.Id },
        ItemToDTO(todoItem));
}

[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return NoContent();
}

private bool TodoItemExists(long id) =>
     _context.TodoItems.Any(e => e.Id == id);

private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
    new TodoItemDTO
    {
        Id = todoItem.Id,
        Name = todoItem.Name,
        IsComplete = todoItem.IsComplete
    };

비밀 필드를 게시하거나 가져올 수 없음을 확인합니다.

JavaScript를 사용하여 웹 API 호출

자습서: JavaScript로 ASP.NET Core 웹 API 호출하기를 참조하세요.

이 자습서에서는 다음과 같은 작업을 수행하는 방법을 살펴봅니다.

  • 웹 API 프로젝트를 만듭니다.
  • 모델 클래스와 데이터베이스 컨텍스트를 추가합니다.
  • CRUD 메서드로 컨트롤러를 스캐폴드합니다.
  • 라우팅, URL 경로 및 반환 값을 구성합니다.
  • Postman을 사용하여 웹 API를 호출합니다.

과정을 마치면 웹 API를 통해 데이터베이스에 저장된 “할 일” 항목을 관리할 수 있습니다.

개요

이 자습서에서는 다음 API를 만듭니다.

API 설명 요청 본문 응답 본문
GET /api/todoitems 할 일 항목 모두 가져오기 없음 할 일 항목의 배열
GET /api/todoitems/{id} ID로 항목 가져오기 없음 할 일 항목
POST /api/todoitems 새 항목 추가 할 일 항목 할 일 항목
PUT /api/todoitems/{id} 기존 항목 업데이트   할 일 항목 없음
DELETE /api/todoitems/{id}     항목 삭제     없음 없음

다음 다이어그램에서는 앱의 디자인을 보여줍니다.

클라이언트는 왼쪽에 상자로 표시됩니다. 클라이언트는 요청을 제출하고 오른쪽에 그려진 상자인 애플리케이션에서 응답을 받습니다. 애플리케이션 상자 내에서 3개의 상자는 컨트롤러, 모델 및 데이터 액세스 계층을 나타냅니다. 요청은 애플리케이션의 컨트롤러로 들어오고 읽기/쓰기 작업은 컨트롤러와 데이터 액세스 계층 간에 발생합니다. 모델은 직렬화되며 응답에서 클라이언트에 반환됩니다.

사전 요구 사항

웹 프로젝트 만들기

  • 파일 메뉴 에서 새로 만들기 > 프로젝트 를 선택합니다.
  • ASP.NET Core 웹 애플리케이션 템플릿을 선택하고 다음 을 클릭합니다.
  • 프로젝트 이름을 TodoApi 로 지정하고 만들기 를 클릭합니다.
  • 새 ASP.NET Core 웹 애플리케이션 만들기 대화 상자에서 .NET CoreASP.NET Core 3.1 이 선택되었는지 확인합니다. API 템플릿을 선택하고 만들기 를 클릭합니다.

VS 새 프로젝트 대화 상자

API 테스트

프로젝트 템플릿은 WeatherForecast API를 만듭니다. 브라우저에서 Get 메서드를 호출하여 앱을 테스트합니다.

Ctrl+F5 키를 눌러 앱을 실행합니다. Visual Studio가 브라우저를 시작하고 https://localhost:<port>/WeatherForecast로 이동합니다. 여기서 <port>는 임의로 선택된 포트 번호입니다.

IIS Express 인증서를 신뢰해야 하는지 묻는 대화 상자가 표시되면 를 선택합니다. 다음으로, 보안 경고 대화 상자가 나타나면 를 선택합니다.

다음과 비슷한 JSON이 반환됩니다.

[
    {
        "date": "2019-07-16T19:04:05.7257911-06:00",
        "temperatureC": 52,
        "temperatureF": 125,
        "summary": "Mild"
    },
    {
        "date": "2019-07-17T19:04:05.7258461-06:00",
        "temperatureC": 36,
        "temperatureF": 96,
        "summary": "Warm"
    },
    {
        "date": "2019-07-18T19:04:05.7258467-06:00",
        "temperatureC": 39,
        "temperatureF": 102,
        "summary": "Cool"
    },
    {
        "date": "2019-07-19T19:04:05.7258471-06:00",
        "temperatureC": 10,
        "temperatureF": 49,
        "summary": "Bracing"
    },
    {
        "date": "2019-07-20T19:04:05.7258474-06:00",
        "temperatureC": -1,
        "temperatureF": 31,
        "summary": "Chilly"
    }
]

모델 클래스 추가

모델 은 앱에서 관리하는 데이터를 나타내는 일련의 클래스입니다. 이 앱에 대한 모델은 단일 TodoItem 클래스입니다.

  • 솔루션 탐색기 에서 프로젝트를 마우스 오른쪽 단추로 클릭합니다. 추가 > 새 폴더 를 선택합니다. 폴더 이름을 Models 로 지정합니다.

  • Models 폴더를 마우스 오른쪽 단추로 클릭하고 추가 > 클래스 를 선택합니다. 클래스 이름을 TodoItem 으로 지정하고 추가 를 선택합니다.

  • 템플릿 코드를 다음 코드로 바꿉니다.

public class TodoItem
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
}

Id 속성은 관계형 데이터베이스에서 고유 키로 작동합니다.

모델 클래스는 프로젝트의 어디로든 이동할 수 있지만 규칙에 따라 Models 폴더를 사용합니다.

데이터베이스 컨텍스트 추가

데이터베이스 컨텍스트 는 데이터 모델에 맞게 Entity Framework 기능을 조정하는 주 클래스입니다. Microsoft.EntityFrameworkCore.DbContext 클래스에서 파생시키는 방식으로 이 클래스를 만듭니다.

NuGet 패키지 추가

  • 도구 메뉴에서 NuGet 패키지 관리자 > 솔루션용 NuGet 패키지 관리 를 선택합니다.
  • 찾아보기 탭을 선택한 다음, 검색 상자에 Microsoft.EntityFrameworkCore.InMemory 를 입력합니다.
  • 왼쪽 창에서 Microsoft.EntityFrameworkCore.InMemory 를 선택합니다.
  • 오른쪽 창에서 프로젝트 확인란을 선택하고 설치 를 선택합니다.

NuGet 패키지 관리자

TodoContext 데이터베이스 컨텍스트 추가

  • Models 폴더를 마우스 오른쪽 단추로 클릭하고 추가 > 클래스 를 선택합니다. 클래스 이름을 TodoContext 로 지정하고 추가 를 클릭합니다.
  • 다음 코드를 입력합니다.

    using Microsoft.EntityFrameworkCore;
    
    namespace TodoApi.Models
    {
        public class TodoContext : DbContext
        {
            public TodoContext(DbContextOptions<TodoContext> options)
                : base(options)
            {
            }
    
            public DbSet<TodoItem> TodoItems { get; set; }
        }
    }
    

데이터베이스 컨텍스트 등록

ASP.NET Core에서는 DB 컨텍스트와 같은 서비스를 DI(종속성 주입) 컨테이너에 등록해야 합니다. 컨테이너는 컨트롤러에 서비스를 제공합니다.

Startup.cs 를 다음 강조 표시된 코드로 업데이트합니다.

// Unused usings removed
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<TodoContext>(opt =>
               opt.UseInMemoryDatabase("TodoList"));
            services.AddControllers();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

위의 코드는

  • 사용되지 않는 using 선언을 제거합니다.
  • DI 컨테이너에 데이터베이스 컨텍스트를 추가합니다.
  • 데이터베이스 컨텍스트가 메모리 내 데이터베이스를 사용하도록 지정합니다.

컨트롤러 스캐폴드

  • Controllers 폴더를 마우스 오른쪽 단추로 클릭합니다.

  • 추가 > 스캐폴드 항목 새로 만들기 를 선택합니다.

  • Entity Framework를 사용하며 동작이 포함된 API 컨트롤러 를 선택하고 추가 를 선택합니다.

  • Entity Framework를 사용하며 동작이 포함된 API 컨트롤러 추가 대화 상자에서:

    • 모델 클래스 에서 TodoItem(TodoApi.Models) 을 선택합니다.
    • 데이터 컨텍스트 클래스 에서 TodoContext(TodoApi.Models) 를 선택합니다.
    • 추가 를 선택합니다.

생성된 코드는:

  • [ApiController] 특성으로 클래스를 표시합니다. 이 특성은 컨트롤러가 웹 API 요청에 응답함을 나타냅니다. 특성을 사용하도록 설정하는 특정 동작에 대한 정보는 ASP.NET Core로 Web API 만들기를 참조하세요.
  • DI를 사용하여 데이터베이스 컨텍스트(TodoContext)를 컨트롤러에 삽입합니다. 컨트롤러의 각 CRUD 메서드에서 해당 데이터베이스 컨텍스트를 사용합니다.

ASP.NET Core 템플릿과 관련해서 다음 사항을 확인합니다.

  • 뷰가 있는 컨트롤러용 ASP.NET Core 템플릿의 경우, 경로 템플릿에 [action]이 포함됩니다.
  • API 컨트롤러용 ASP.NET Core 템플릿의 경우, 경로 템플릿에 [action]이 포함되지 않습니다.

[action] 토큰이 경로 템플릿에 없는 경우 경로에서 작업 이름이 제외됩니다. 즉, 일치하는 경로에서 작업과 연결된 메서드 이름이 사용되지 않습니다.

PostTodoItem 만들기 메서드 검사

PostTodoItem의 return 문이 nameof 연산자를 사용하도록 바꿉니다.

// POST: api/TodoItems
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    //return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}

위의 코드는 [HttpPost] 특성으로 표시되는 HTTP POST 메서드입니다. 이 메서드는 HTTP 요청 본문에서 할 일 항목 값을 가져옵니다.

자세한 내용은 Http[동사] 특성을 사용한 특성 라우팅을 참조하세요.

CreatedAtAction 메서드는 다음 작업을 수행합니다.

  • 성공 시 HTTP 201 상태 코드를 반환합니다. HTTP 201은 서버에서 새 리소스를 만드는 HTTP POST 메서드의 표준 응답입니다.
  • 응답에 대한 위치 헤더를 추가합니다. Location 헤더는 새로 만들어진 할 일 항목의 URI를 지정합니다. 자세한 내용은 10.2.2 201 생성됨을 참조하세요.
  • Location 헤더의 URI를 만들려면 GetTodoItem 작업을 참조합니다. C# nameof 키워드는 CreatedAtAction 호출에서 작업 이름의 하드 코딩을 방지하는 데 사용됩니다.

Postman을 설치합니다.

이 자습서에서는 Postman을 사용하여 웹 API를 테스트합니다.

  • Postman을 설치합니다.
  • 웹앱을 시작합니다.
  • Postman을 시작합니다.
  • SSL 인증서 확인 을 사용하지 않도록 설정합니다.
    • 파일 > 설정(일반 탭)에서 SSL 인증서 확인 을 사용하지 않도록 설정합니다.

      경고

      컨트롤러를 테스트한 후에 SSL 인증서 확인을 다시 사용하도록 설정합니다.

Postman을 사용하여 PostTodoItem 테스트

  • 새 요청을 만듭니다.

  • HTTP 메서드를 POST로 설정합니다.

  • URI를 https://localhost:<port>/api/todoitems로 설정합니다. 예: https://localhost:5001/api/todoitems.

  • 본문 탭을 선택합니다.

  • 원시 라디오 단추를 선택합니다.

  • 유형을 JSON(application/json) 으로 설정합니다.

  • 요청 본문에서 할 일 항목에 대한 JSON을 입력합니다.

    {
      "name":"walk dog",
      "isComplete":true
    }
    
  • 보내기 를 선택합니다.

    생성 요청이 있는 Postman

Postman을 사용하여 위치 헤더 URI 테스트

  • 응답 창에서 헤더 탭을 선택합니다.

  • 위치 헤더 값을 복사합니다.

    Postman 콘솔의 헤더 탭

  • HTTP 메서드를 GET로 설정합니다.

  • URI를 https://localhost:<port>/api/todoitems/1로 설정합니다. 예: https://localhost:5001/api/todoitems/1.

  • 보내기 를 선택합니다.

GET 메서드 검사

다음과 같은 메서드는 두 개의 GET 엔드포인트를 구현합니다.

  • GET /api/todoitems
  • GET /api/todoitems/{id}

브라우저 또는 Postman에서 두 개의 엔드포인트를 호출하여 앱을 테스트합니다. 예를 들어:

  • https://localhost:5001/api/todoitems
  • https://localhost:5001/api/todoitems/1

GetTodoItems를 호출하면 다음과 비슷한 응답이 생성됩니다.

[
  {
    "id": 1,
    "name": "Item1",
    "isComplete": false
  }
]

Postman을 사용하여 Get 테스트

  • 새 요청을 만듭니다.
  • HTTP 메서드를 GET 으로 설정합니다.
  • 요청 URI를 https://localhost:<port>/api/todoitems로 설정합니다. 예: https://localhost:5001/api/todoitems.
  • Postman에서 두 개의 창 보기 를 설정합니다.
  • 보내기 를 선택합니다.

이 앱은 메모리 내 데이터베이스를 사용합니다. 앱이 중지된 후 시작되면 이전 GET 요청이 데이터를 반환하지 않습니다. 데이터가 반환되지 않으면 앱에 데이터를 POST합니다.

라우팅 및 URL 경로

[HttpGet] 특성은 HTTP GET 요청에 응답하는 메서드를 나타냅니다. 각 방법에 대한 URL 경로는 다음과 같이 구성됩니다.

  • 컨트롤러의 Route 특성에서 템플릿 문자열로 시작합니다.

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    {
        private readonly TodoContext _context;
    
        public TodoItemsController(TodoContext context)
        {
            _context = context;
        }
    
  • [controller]를 컨트롤러의 이름으로 바꿉니다. 일반적으로 컨트롤러 클래스 이름에서 "Controller" 접미사를 뺀 이름입니다. 이 샘플의 경우 컨트롤러 클래스 이름은 TodoItems Controller이므로 컨트롤러 이름은 “TodoItems”입니다. ASP.NET Core 라우팅은 대/소문자를 구분하지 않습니다.

  • [HttpGet] 특성에 경로 템플릿(예: [HttpGet("products")])이 있는 경우 경로에 추가합니다. 이 샘플은 템플릿을 사용하지 않습니다. 자세한 내용은 Http[동사] 특성을 사용한 특성 라우팅을 참조하세요.

다음 GetTodoItem 메서드에서 "{id}"는 할 일 항목의 고유 식별자에 대한 자리 표시자 변수입니다. GetTodoItem이 호출되면 URL의 "{id}" 값을 id 매개 변수의 메서드에 제공합니다.

// GET: api/TodoItems/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

반환 값

GetTodoItemsGetTodoItem 메서드의 반환 형식은 ActionResult<T> 형식입니다. ASP.NET Core는 자동으로 JSON에 개체를 직렬화하고 JSON을 응답 메시지의 본문에 기록합니다. 이 반환 형식의 응답 코드는 200이며 처리되지 않은 예외가 없다고 가정합니다. 처리되지 않은 예외는 5xx 오류로 변환됩니다.

ActionResult 반환 형식은 다양한 HTTP 상태 코드를 나타낼 수 있습니다. 예를 들어 GetTodoItem은 두 가지 상태 값을 반환할 수 있습니다.

  • 요청된 ID와 일치하는 항목이 없는 경우 메서드가 404 NotFound 오류 코드를 반환합니다.
  • 그렇지 않으면 메서드가 JSON 응답 본문에서 200을 반환합니다. item을 반환하면 HTTP 200 응답이 발생합니다.

PutTodoItem 메서드

다음과 같이 PutTodoItem 메서드를 검사합니다.

// PUT: api/TodoItems/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
    if (id != todoItem.Id)
    {
        return BadRequest();
    }

    _context.Entry(todoItem).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!TodoItemExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}

HTTP PUT을 사용하는 것을 제외하고 PutTodoItemPostTodoItem와 비슷합니다. 응답은 204(콘텐츠 없음)입니다. HTTP 사양에 따라 PUT 요청의 경우 클라이언트는 변경 내용만이 아니라 전체 업데이트된 엔터티를 보내야 합니다. 부분 업데이트를 지원하려면 HTTP PATCH를 사용합니다.

PutTodoItem을 호출하는 중 오류가 발생하면 GET을 호출하여 데이터베이스에 항목이 있는지 확인합니다.

PutTodoItem 메서드 테스트

이 샘플은 앱이 시작될 때마다 초기화되어야 하는 메모리 내 데이터베이스를 사용합니다. PUT 호출을 실행하기 전에 데이터베이스에 항목이 있어야 합니다. GET을 호출하여 PUT 호출을 실행하기 전에 데이터베이스에 항목이 있는지 확인합니다.

ID = 1인 할 일 항목을 업데이트하고 해당 이름을 “feed fish”로 설정합니다.

  {
    "id":1,
    "name":"feed fish",
    "isComplete":true
  }

다음 이미지는 Postman 업데이트를 보여줍니다.

204(콘텐츠 없음) 응답을 보여주는 Postman 콘솔

DeleteTodoItem 메서드

다음과 같이 DeleteTodoItem 메서드를 검사합니다.

// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<ActionResult<TodoItem>> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return todoItem;
}

DeleteTodoItem 메서드 테스트

Postman을 사용하여 할 일 항목을 삭제합니다.

  • 메서드를 DELETE로 설정합니다.
  • 예를 들어 삭제할 개체의 URI를 https://localhost:5001/api/todoitems/1로 설정합니다.
  • 보내기 를 선택합니다.

과도한 게시 방지

현재 샘플 앱은 전체 TodoItem 개체를 공개합니다. 일반적으로 프로덕션 앱은 모델의 하위 집합을 사용하여 입력 및 반환되는 데이터를 제한합니다. 이 동작에는 여러 가지 이유가 있으며, 보안이 주요 이유 중 하나입니다. 일반적으로 모델의 하위 집합을 DTO(데이터 전송 개체), 입력 모델 또는 뷰 모델이라고 합니다. 이 문서에서는 DTO 를 사용합니다.

DTO는 다음과 같은 용도로 사용할 수 있습니다.

  • 과도한 게시를 방지합니다.
  • 클라이언트에 표시되지 않아야 하는 속성을 숨깁니다.
  • 페이로드 크기를 줄이기 위해 일부 속성을 생략합니다.
  • 중첩된 개체를 포함하는 개체 그래프를 평면화합니다. 클라이언트에는 평면화된 개체 그래프가 더 편리할 수 있습니다.

DTO 방법을 설명하려면 비밀 필드를 포함하도록 TodoItem 클래스를 업데이트합니다.

public class TodoItem
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
    public string Secret { get; set; }
}

이 앱에서는 숨겨진 필드를 숨겨야 하지만, 관리 앱은 숨겨진 필드를 공개할 수 있습니다.

비밀 필드를 게시하고 가져올 수 있는지 확인합니다.

DTO 모델을 만듭니다.

public class TodoItemDTO
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
}

TodoItemDTO를 사용하도록 TodoItemsController를 업데이트합니다.

    [HttpGet]
    public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
    {
        return await _context.TodoItems
            .Select(x => ItemToDTO(x))
            .ToListAsync();
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);

        if (todoItem == null)
        {
            return NotFound();
        }

        return ItemToDTO(todoItem);
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
    {
        if (id != todoItemDTO.Id)
        {
            return BadRequest();
        }

        var todoItem = await _context.TodoItems.FindAsync(id);
        if (todoItem == null)
        {
            return NotFound();
        }

        todoItem.Name = todoItemDTO.Name;
        todoItem.IsComplete = todoItemDTO.IsComplete;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
        {
            return NotFound();
        }

        return NoContent();
    }

    [HttpPost]
    public async Task<ActionResult<TodoItemDTO>> CreateTodoItem(TodoItemDTO todoItemDTO)
    {
        var todoItem = new TodoItem
        {
            IsComplete = todoItemDTO.IsComplete,
            Name = todoItemDTO.Name
        };

        _context.TodoItems.Add(todoItem);
        await _context.SaveChangesAsync();

        return CreatedAtAction(
            nameof(GetTodoItem),
            new { id = todoItem.Id },
            ItemToDTO(todoItem));
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteTodoItem(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);

        if (todoItem == null)
        {
            return NotFound();
        }

        _context.TodoItems.Remove(todoItem);
        await _context.SaveChangesAsync();

        return NoContent();
    }

    private bool TodoItemExists(long id) =>
         _context.TodoItems.Any(e => e.Id == id);

    private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
        new TodoItemDTO
        {
            Id = todoItem.Id,
            Name = todoItem.Name,
            IsComplete = todoItem.IsComplete
        };       
}

비밀 필드를 게시하거나 가져올 수 없음을 확인합니다.

JavaScript를 사용하여 웹 API 호출

자습서: JavaScript로 ASP.NET Core 웹 API 호출하기를 참조하세요.

이 자습서에서는 다음과 같은 작업을 수행하는 방법을 살펴봅니다.

  • 웹 API 프로젝트를 만듭니다.
  • 모델 클래스와 데이터베이스 컨텍스트를 추가합니다.
  • 컨트롤러 추가
  • CRUD 메서드 추가
  • 라우팅 및 URL 경로 구성
  • 반환 값 지정
  • Postman을 사용하여 웹 API를 호출합니다.
  • JavaScript를 사용하여 웹 API를 호출합니다.

작업을 완료하면 웹 API는 관계형 데이터베이스에 저장된 "할 일" 항목을 관리할 수 있게 됩니다.

개요 2.1

이 자습서에서는 다음 API를 만듭니다.

API 설명 요청 본문 응답 본문
GET /api/todoitems 할 일 항목 모두 가져오기 없음 할 일 항목의 배열
GET /api/todoitems/{id} ID로 항목 가져오기 없음 할 일 항목
POST /api/todoitems 새 항목 추가 할 일 항목 할 일 항목
PUT /api/todoitems/{id} 기존 항목 업데이트   할 일 항목 없음
DELETE /api/todoitems/{id}     항목 삭제     없음 없음

다음 다이어그램에서는 앱의 디자인을 보여줍니다.

클라이언트는 왼쪽에 상자로 표시됩니다. 클라이언트는 요청을 제출하고 오른쪽에 그려진 상자인 애플리케이션에서 응답을 받습니다. 애플리케이션 상자 내에서 3개의 상자는 컨트롤러, 모델 및 데이터 액세스 계층을 나타냅니다. 요청은 애플리케이션의 컨트롤러로 들어오고 읽기/쓰기 작업은 컨트롤러와 데이터 액세스 계층 간에 발생합니다. 모델은 직렬화되며 응답에서 클라이언트에 반환됩니다.

필수 구성 요소 2.1

경고

Visual Studio 2017을 사용하는 경우 Visual Studio에서 작동하지 않는 .NET Core SDK 버전에 대한 자세한 내용은 dotnet/sdk issue #3124(dotnet/sdk 문제 #3124)를 참조하세요.

웹 프로젝트 만들기 2.1

  • 파일 메뉴 에서 새로 만들기 > 프로젝트 를 선택합니다.
  • ASP.NET Core 웹 애플리케이션 템플릿을 선택하고 다음 을 클릭합니다.
  • 프로젝트 이름을 TodoApi 로 지정하고 만들기 를 클릭합니다.
  • 새 ASP.NET Core 웹 애플리케이션 만들기 대화 상자에서 .NET CoreASP.NET Core 2.2 가 선택되었는지 확인합니다. API 템플릿을 선택하고 만들기 를 클릭합니다. Docker 지원 사용 을 선택하지 마세요.

VS 새 프로젝트 대화 상자

API 테스트 2.1

프로젝트 템플릿은 values API를 만듭니다. 브라우저에서 Get 메서드를 호출하여 앱을 테스트합니다.

Ctrl+F5 키를 눌러 앱을 실행합니다. Visual Studio가 브라우저를 시작하고 https://localhost:<port>/api/values로 이동합니다. 여기서 <port>는 임의로 선택된 포트 번호입니다.

IIS Express 인증서를 신뢰해야 하는지 묻는 대화 상자가 표시되면 를 선택합니다. 다음으로, 보안 경고 대화 상자가 나타나면 를 선택합니다.

다음 JSON이 반환됩니다.

["value1","value2"]

모델 클래스 추가 2.1

모델 은 앱에서 관리하는 데이터를 나타내는 일련의 클래스입니다. 이 앱에 대한 모델은 단일 TodoItem 클래스입니다.

  • 솔루션 탐색기 에서 프로젝트를 마우스 오른쪽 단추로 클릭합니다. 추가 > 새 폴더 를 선택합니다. 폴더 이름을 Models 로 지정합니다.

  • Models 폴더를 마우스 오른쪽 단추로 클릭하고 추가 > 클래스 를 선택합니다. 클래스 이름을 TodoItem 으로 지정하고 추가 를 선택합니다.

  • 템플릿 코드를 다음 코드로 바꿉니다.

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public bool IsComplete { get; set; }
    }
}

Id 속성은 관계형 데이터베이스에서 고유 키로 작동합니다.

모델 클래스는 프로젝트의 어디로든 이동할 수 있지만 규칙에 따라 Models 폴더를 사용합니다.

데이터베이스 컨텍스트 추가 2.1

데이터베이스 컨텍스트 는 데이터 모델에 맞게 Entity Framework 기능을 조정하는 주 클래스입니다. Microsoft.EntityFrameworkCore.DbContext 클래스에서 파생시키는 방식으로 이 클래스를 만듭니다.

  • Models 폴더를 마우스 오른쪽 단추로 클릭하고 추가 > 클래스 를 선택합니다. 클래스 이름을 TodoContext 로 지정하고 추가 를 클릭합니다.
  • 템플릿 코드를 다음 코드로 바꿉니다.

    using Microsoft.EntityFrameworkCore;
    
    namespace TodoApi.Models
    {
        public class TodoContext : DbContext
        {
            public TodoContext(DbContextOptions<TodoContext> options)
                : base(options)
            {
            }
    
            public DbSet<TodoItem> TodoItems { get; set; }
        }
    }
    

데이터베이스 컨텍스트 등록 2.1

ASP.NET Core에서는 DB 컨텍스트와 같은 서비스를 DI(종속성 주입) 컨테이너에 등록해야 합니다. 컨테이너는 컨트롤러에 서비스를 제공합니다.

Startup.cs 를 다음 강조 표시된 코드로 업데이트합니다.

// Unused usings removed
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using TodoApi.Models;

namespace TodoApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the 
        //container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<TodoContext>(opt =>
                opt.UseInMemoryDatabase("TodoList"));
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP 
        //request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                // The default HSTS value is 30 days. You may want to change this for 
                // production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }
}

위의 코드는

  • 사용되지 않는 using 선언을 제거합니다.
  • DI 컨테이너에 데이터베이스 컨텍스트를 추가합니다.
  • 데이터베이스 컨텍스트가 메모리 내 데이터베이스를 사용하도록 지정합니다.

컨트롤러 추가 2.1

  • Controllers 폴더를 마우스 오른쪽 단추로 클릭합니다.

  • 추가 > 새 항목 을 선택합니다.

  • 새 항목 추가 대화 상자에서 API 컨트롤러 클래스 템플릿을 선택합니다.

  • 클래스 이름을 TodoController 로 지정하고 추가 를 선택합니다.

    검색 상자의 컨트롤러 및 웹 API 컨트롤러가 선택된 새 항목 추가 대화 상자

  • 템플릿 코드를 다음 코드로 바꿉니다.

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.EntityFrameworkCore;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using TodoApi.Models;
    
    namespace TodoApi.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class TodoController : ControllerBase
        {
            private readonly TodoContext _context;
    
            public TodoController(TodoContext context)
            {
                _context = context;
    
                if (_context.TodoItems.Count() == 0)
                {
                    // Create a new TodoItem if collection is empty,
                    // which means you can't delete all TodoItems.
                    _context.TodoItems.Add(new TodoItem { Name = "Item1" });
                    _context.SaveChanges();
                }
            }
        }
    }
    

위의 코드는

  • 메서드 없이 API 컨트롤러 클래스를 정의합니다.
  • [ApiController] 특성으로 클래스를 표시합니다. 이 특성은 컨트롤러가 웹 API 요청에 응답함을 나타냅니다. 특성을 사용하도록 설정하는 특정 동작에 대한 정보는 ASP.NET Core로 Web API 만들기를 참조하세요.
  • DI를 사용하여 데이터베이스 컨텍스트(TodoContext)를 컨트롤러에 삽입합니다. 컨트롤러의 각 CRUD 메서드에서 해당 데이터베이스 컨텍스트를 사용합니다.
  • 데이터베이스가 비어 있는 경우 데이터베이스에 Item1이라는 항목을 추가합니다. 이 코드는 생성자에 위치하므로 새 HTTP 요청이 발생할 때마다 실행됩니다. 모든 항목을 삭제하면 생성자는 다음에 API가 호출될 경우 Item1을 다시 만듭니다. 따라서 실제로 작동되는 경우 삭제가 작동하지 않는 것처럼 보일 수 있습니다.

GET 메서드 추가 2.1

할 일 항목을 가져오는 API를 제공하려면 다음 메서드를 TodoController 클래스에 추가합니다.

// GET: api/Todo
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems()
{
    return await _context.TodoItems.ToListAsync();
}

// GET: api/Todo/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

다음과 같은 메서드는 두 개의 GET 엔드포인트를 구현합니다.

  • GET /api/todo
  • GET /api/todo/{id}

앱이 계속 실행되고 있으면 중지합니다. 그런 다음, 다시 실행하여 최신 변경 내용을 포함합니다.

브라우저에서 두 개의 엔드포인트를 호출하여 앱을 테스트합니다. 예를 들어:

  • https://localhost:<port>/api/todo
  • https://localhost:<port>/api/todo/1

GetTodoItems를 호출하여 다음 HTTP 응답이 생성됩니다.

[
  {
    "id": 1,
    "name": "Item1",
    "isComplete": false
  }
]

라우팅 및 URL 경로 2.1

[HttpGet] 특성은 HTTP GET 요청에 응답하는 메서드를 나타냅니다. 각 방법에 대한 URL 경로는 다음과 같이 구성됩니다.

  • 컨트롤러의 Route 특성에서 템플릿 문자열로 시작합니다.

    namespace TodoApi.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class TodoController : ControllerBase
        {
            private readonly TodoContext _context;
    
  • [controller]를 컨트롤러의 이름으로 바꿉니다. 일반적으로 컨트롤러 클래스 이름에서 "Controller" 접미사를 뺀 이름입니다. 이 샘플의 경우 컨트롤러 클래스 이름은 Todo Controller이므로 컨트롤러 이름은 "todo"입니다. ASP.NET Core 라우팅은 대/소문자를 구분하지 않습니다.

  • [HttpGet] 특성에 경로 템플릿(예: [HttpGet("products")])이 있는 경우 경로에 추가합니다. 이 샘플은 템플릿을 사용하지 않습니다. 자세한 내용은 Http[동사] 특성을 사용한 특성 라우팅을 참조하세요.

다음 GetTodoItem 메서드에서 "{id}"는 할 일 항목의 고유 식별자에 대한 자리 표시자 변수입니다. GetTodoItem가 호출되면 URL의 "{id}" 값을 id 매개 변수의 메서드에 제공합니다.

// GET: api/Todo/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

반환 값 2.1

GetTodoItemsGetTodoItem 메서드의 반환 형식은 ActionResult<T> 형식입니다. ASP.NET Core는 자동으로 JSON에 개체를 직렬화하고 JSON을 응답 메시지의 본문에 기록합니다. 이 반환 형식의 응답 코드는 200이며 처리되지 않은 예외가 없다고 가정합니다. 처리되지 않은 예외는 5xx 오류로 변환됩니다.

ActionResult 반환 형식은 다양한 HTTP 상태 코드를 나타낼 수 있습니다. 예를 들어 GetTodoItem은 두 가지 상태 값을 반환할 수 있습니다.

  • 요청된 ID와 일치하는 항목이 없는 경우 메서드가 404 NotFound 오류 코드를 반환합니다.
  • 그렇지 않으면 메서드가 JSON 응답 본문에서 200을 반환합니다. item을 반환하면 HTTP 200 응답이 발생합니다.

GetTodoItems 메서드 테스트 2.1

이 자습서에서는 Postman을 사용하여 웹 API를 테스트합니다.

  • Postman을 설치합니다.
  • 웹앱을 시작합니다.
  • Postman을 시작합니다.
  • SSL 인증서 확인 을 사용하지 않도록 설정합니다.
  • 파일 > 설정(일반 탭)에서 SSL 인증서 확인 을 사용하지 않도록 설정합니다.

경고

컨트롤러를 테스트한 후에 SSL 인증서 확인을 다시 사용하도록 설정합니다.

  • 새 요청을 만듭니다.
    • HTTP 메서드를 GET 으로 설정합니다.
    • 요청 URI를 https://localhost:<port>/api/todo로 설정합니다. 예: https://localhost:5001/api/todo.
  • Postman에서 두 개의 창 보기 를 설정합니다.
  • 보내기 를 선택합니다.

Get 요청이 있는 Postman

Create 메서드 추가 2.1

Controllers/TodoController.cs 내부에 다음 PostTodoItem 메서드를 추가합니다.

// POST: api/Todo
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem item)
{
    _context.TodoItems.Add(item);
    await _context.SaveChangesAsync();

    return CreatedAtAction(nameof(GetTodoItem), new { id = item.Id }, item);
}

위의 코드는 [HttpPost] 특성으로 표시되는 HTTP POST 메서드입니다. 이 메서드는 HTTP 요청 본문에서 할 일 항목 값을 가져옵니다.

CreatedAtAction 메서드는 다음 작업을 수행합니다.

  • 성공 시 HTTP 201 상태 코드를 반환합니다. HTTP 201은 서버에서 새 리소스를 만드는 HTTP POST 메서드의 표준 응답입니다.

  • Location 헤더를 응답에 추가합니다. Location 헤더는 새로 만들어진 할 일 항목의 URI를 지정합니다. 자세한 내용은 10.2.2 201 생성됨을 참조하세요.

  • Location 헤더의 URI를 만들려면 GetTodoItem 작업을 참조합니다. C# nameof 키워드는 CreatedAtAction 호출에서 작업 이름의 하드 코딩을 방지하는 데 사용됩니다.

    // GET: api/Todo/5
    [HttpGet("{id}")]
    public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);
    
        if (todoItem == null)
        {
            return NotFound();
        }
    
        return todoItem;
    }
    

PostTodoItem 메서드 테스트 2.1

  • 프로젝트를 빌드합니다.

  • Postman에서 HTTP 메서드를 POST로 설정합니다.

  • URI를 https://localhost:<port>/api/Todo으로 설정합니다. 예: https://localhost:5001/api/Todo.

  • 본문 탭을 선택합니다.

  • 원시 라디오 단추를 선택합니다.

  • 유형을 JSON(application/json) 으로 설정합니다.

  • 요청 본문에서 할 일 항목에 대한 JSON을 입력합니다.

    {
      "name":"walk dog",
      "isComplete":true
    }
    
  • 보내기 를 선택합니다.

    생성 요청이 있는 Postman

    405 메서드가 허용되지 않음 오류가 발생할 경우 PostTodoItem 메서드를 추가한 후에 프로젝트를 컴파일하지 않아서 발생한 결과일 수 있습니다.

위치 헤더 URI 테스트 2.1

  • 응답 창에서 헤더 탭을 선택합니다.

  • 위치 헤더 값을 복사합니다.

    Postman 콘솔의 헤더 탭

  • 메서드를 GET으로 설정합니다.

  • URI를 https://localhost:<port>/api/todoitems/2으로 설정합니다. 예: https://localhost:5001/api/todoitems/2.

  • 보내기 를 선택합니다.

PutTodoItem 메서드 추가 2.1

다음 PutTodoItem 메서드를 추가합니다.

// PUT: api/Todo/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem item)
{
    if (id != item.Id)
    {
        return BadRequest();
    }

    _context.Entry(item).State = EntityState.Modified;
    await _context.SaveChangesAsync();

    return NoContent();
}

HTTP PUT을 사용하는 것을 제외하고 PutTodoItemPostTodoItem와 비슷합니다. 응답은 204(콘텐츠 없음)입니다. HTTP 사양에 따라 PUT 요청의 경우 클라이언트는 변경 내용만이 아니라 전체 업데이트된 엔터티를 보내야 합니다. 부분 업데이트를 지원하려면 HTTP PATCH를 사용합니다.

PutTodoItem을 호출하는 중 오류가 발생하면 GET을 호출하여 데이터베이스에 항목이 있는지 확인합니다.

PutTodoItem 메서드 테스트 2.1

이 샘플은 앱이 시작될 때마다 초기화되어야 하는 메모리 내 데이터베이스를 사용합니다. PUT 호출을 실행하기 전에 데이터베이스에 항목이 있어야 합니다. GET을 호출하여 PUT 호출을 실행하기 전에 데이터베이스에 항목이 있는지 확인합니다.

ID = 1인 할 일 항목을 업데이트하고 해당 이름을 “feed fish”로 설정합니다.

  {
    "id":1,
    "name":"feed fish",
    "isComplete":true
  }

다음 이미지는 Postman 업데이트를 보여줍니다.

204(콘텐츠 없음) 응답을 보여주는 Postman 콘솔

DeleteTodoItem 메서드 추가 2.1

다음 DeleteTodoItem 메서드를 추가합니다.

// DELETE: api/Todo/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return NoContent();
}

DeleteTodoItem 응답은 204(콘텐츠 없음)입니다.

DeleteTodoItem 메서드 테스트 2.1

Postman을 사용하여 할 일 항목을 삭제합니다.

  • 메서드를 DELETE로 설정합니다.
  • 삭제할 개체의 URI를 설정합니다(예: https://localhost:5001/api/todo/1).
  • 보내기 를 선택합니다.

샘플 앱을 사용하면 모든 항목을 삭제할 수 있습니다. 하지만 마지막 항목이 삭제되면 다음에 API를 호출하는 경우 모델 클래스 생성자에서 새로운 항목이 생성됩니다.

JavaScript를 사용하여 웹 API 호출 2.1

이 섹션에는 JavaScript를 사용하여 웹 API를 호출하는 HTML 페이지가 추가되었습니다. jQuery가 요청을 시작합니다. JavaScript는 웹 API 응답의 세부 정보를 토대로 페이지를 업데이트합니다.

다음 강조 표시된 코드로 Startup.cs 를 업데이트하여 앱이 정적 파일을 제공하고 기본 파일 매핑을 사용하도록 구성합니다.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        // The default HSTS value is 30 days. You may want to change this for 
        // production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }

    app.UseDefaultFiles();
    app.UseStaticFiles();
    app.UseHttpsRedirection();
    app.UseMvc();
}

프로젝트 디렉터리에서 wwwroot 폴더를 만듭니다.

index.html 이라는 HTML 파일을 wwwroot 디렉터리에 추가합니다. 다음 표시로 콘텐츠를 바꿉니다.

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>To-do CRUD</title>
    <style>
        input[type='submit'], button, [aria-label] {
            cursor: pointer;
        }

        #spoiler {
            display: none;
        }

        table {
            font-family: Arial, sans-serif;
            border: 1px solid;
            border-collapse: collapse;
        }

        th {
            background-color: #0066CC;
            color: white;
        }

        td {
            border: 1px solid;
            padding: 5px;
        }
    </style>
</head>
<body>
    <h1>To-do CRUD</h1>
    <h3>Add</h3>
    <form action="javascript:void(0);" method="POST" onsubmit="addItem()">
        <input type="text" id="add-name" placeholder="New to-do">
        <input type="submit" value="Add">
    </form>

    <div id="spoiler">
        <h3>Edit</h3>
        <form class="my-form">
            <input type="hidden" id="edit-id">
            <input type="checkbox" id="edit-isComplete">
            <input type="text" id="edit-name">
            <input type="submit" value="Save">
            <a onclick="closeInput()" aria-label="Close">&#10006;</a>
        </form>
    </div>

    <p id="counter"></p>

    <table>
        <tr>
            <th>Is Complete</th>
            <th>Name</th>
            <th></th>
            <th></th>
        </tr>
        <tbody id="todos"></tbody>
    </table>

    <script src="https://code.jquery.com/jquery-3.3.1.min.js"
            integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
            crossorigin="anonymous"></script>
    <script src="site.js"></script>
</body>
</html>

site.js 라는 JavaScript 파일을 wwwroot 디렉터리에 추가합니다. 다음 코드로 콘텐츠를 바꿉니다.

const uri = "api/todo";
let todos = null;
function getCount(data) {
  const el = $("#counter");
  let name = "to-do";
  if (data) {
    if (data > 1) {
      name = "to-dos";
    }
    el.text(data + " " + name);
  } else {
    el.text("No " + name);
  }
}

$(document).ready(function() {
  getData();
});

function getData() {
  $.ajax({
    type: "GET",
    url: uri,
    cache: false,
    success: function(data) {
      const tBody = $("#todos");

      $(tBody).empty();

      getCount(data.length);

      $.each(data, function(key, item) {
        const tr = $("<tr></tr>")
          .append(
            $("<td></td>").append(
              $("<input/>", {
                type: "checkbox",
                disabled: true,
                checked: item.isComplete
              })
            )
          )
          .append($("<td></td>").text(item.name))
          .append(
            $("<td></td>").append(
              $("<button>Edit</button>").on("click", function() {
                editItem(item.id);
              })
            )
          )
          .append(
            $("<td></td>").append(
              $("<button>Delete</button>").on("click", function() {
                deleteItem(item.id);
              })
            )
          );

        tr.appendTo(tBody);
      });

      todos = data;
    }
  });
}

function addItem() {
  const item = {
    name: $("#add-name").val(),
    isComplete: false
  };

  $.ajax({
    type: "POST",
    accepts: "application/json",
    url: uri,
    contentType: "application/json",
    data: JSON.stringify(item),
    error: function(jqXHR, textStatus, errorThrown) {
      alert("Something went wrong!");
    },
    success: function(result) {
      getData();
      $("#add-name").val("");
    }
  });
}

function deleteItem(id) {
  $.ajax({
    url: uri + "/" + id,
    type: "DELETE",
    success: function(result) {
      getData();
    }
  });
}

function editItem(id) {
  $.each(todos, function(key, item) {
    if (item.id === id) {
      $("#edit-name").val(item.name);
      $("#edit-id").val(item.id);
      $("#edit-isComplete")[0].checked = item.isComplete;
    }
  });
  $("#spoiler").css({ display: "block" });
}

$(".my-form").on("submit", function() {
  const item = {
    name: $("#edit-name").val(),
    isComplete: $("#edit-isComplete").is(":checked"),
    id: $("#edit-id").val()
  };

  $.ajax({
    url: uri + "/" + $("#edit-id").val(),
    type: "PUT",
    accepts: "application/json",
    contentType: "application/json",
    data: JSON.stringify(item),
    success: function(result) {
      getData();
    }
  });

  closeInput();
  return false;
});

function closeInput() {
  $("#spoiler").css({ display: "none" });
}

HTML 페이지를 로컬에서 테스트하려면 ASP.NET Core 프로젝트의 시작 설정을 변경해야 할 수 있습니다.

  • Properties\launchSettings.json 을 엽니다.
  • launchUrl 속성을 제거하여 앱이 index.html— 프로젝트의 기본 파일에서 열리도록 합니다.

이 샘플은 웹 API의 CRUD 메서드를 모두 호출합니다. API 호출에 대한 설명은 다음과 같습니다.

할 일 항목의 목록 가져오기 2.1

jQuery는 할 일 항목의 배열을 나타내는 JSON을 반환하는 웹 API에 HTTP GET 요청을 보냅니다. 요청이 성공하면 success 콜백 함수가 호출됩니다. 콜백에서 DOM은 할 일 정보로 업데이트됩니다.

$(document).ready(function() {
  getData();
});

function getData() {
  $.ajax({
    type: "GET",
    url: uri,
    cache: false,
    success: function(data) {
      const tBody = $("#todos");

      $(tBody).empty();

      getCount(data.length);

      $.each(data, function(key, item) {
        const tr = $("<tr></tr>")
          .append(
            $("<td></td>").append(
              $("<input/>", {
                type: "checkbox",
                disabled: true,
                checked: item.isComplete
              })
            )
          )
          .append($("<td></td>").text(item.name))
          .append(
            $("<td></td>").append(
              $("<button>Edit</button>").on("click", function() {
                editItem(item.id);
              })
            )
          )
          .append(
            $("<td></td>").append(
              $("<button>Delete</button>").on("click", function() {
                deleteItem(item.id);
              })
            )
          );

        tr.appendTo(tBody);
      });

      todos = data;
    }
  });
}

할 일 항목 추가 2.1

jQuery는 요청 본문에 있는 할 일 항목을 사용하여 HTTP POST 요청을 보냅니다. acceptscontentType 옵션은 수신 및 전송되는 미디어 형식을 지정하기 위해 application/json으로 설정됩니다. JSON.stringify를 사용하여 할 일 항목을 JSON으로 변환합니다. API가 성공적인 상태 코드를 반환하면 getData 함수가 호출되어 HTML 테이블을 업데이트합니다.

function addItem() {
  const item = {
    name: $("#add-name").val(),
    isComplete: false
  };

  $.ajax({
    type: "POST",
    accepts: "application/json",
    url: uri,
    contentType: "application/json",
    data: JSON.stringify(item),
    error: function(jqXHR, textStatus, errorThrown) {
      alert("Something went wrong!");
    },
    success: function(result) {
      getData();
      $("#add-name").val("");
    }
  });
}

할 일 항목 업데이트 2.1

할 일 항목을 업데이트하는 작업은 추가하는 작업과 비슷합니다. url은 항목의 고유 식별자를 추가하도록 변경되고 typePUT입니다.

$.ajax({
  url: uri + "/" + $("#edit-id").val(),
  type: "PUT",
  accepts: "application/json",
  contentType: "application/json",
  data: JSON.stringify(item),
  success: function(result) {
    getData();
  }
});

할 일 항목 삭제 2.1

할 일 항목을 삭제하려면 AJAX 호출에서 typeDELETE로 설정하고 URL에서 항목의 고유 식별자를 지정하면 됩니다.

$.ajax({
  url: uri + "/" + id,
  type: "DELETE",
  success: function(result) {
    getData();
  }
});

웹 API에 인증 지원 추가 2.1

ASP.NET Core Identity는 ASP.NET Core 웹앱에 UI(사용자 인터페이스) 로그인 기능을 추가합니다. 웹 API 및 SPA를 보호하려면 다음 중 하나를 사용합니다.

IdentityServer4는 ASP.NET Core용 OpenID Connect 및 OAuth 2.0 프레임워크입니다. IdentityServer4에서는 다음과 같은 보안 기능을 사용할 수 있습니다.

  • AaaS(Authentication as a Service)
  • 여러 응용 프로그램 유형에 대한 SSO(Single Sign-On/Off)
  • API에 대한 액세스 제어
  • 페더레이션 게이트웨이

자세한 내용은 IdentityServer4 시작을 참조하세요.

추가 리소스 2.1

이 자습서에서 샘플 코드 보기 또는 다운로드 다운로드하는 방법을 참조하세요.

자세한 내용은 다음 자료를 참조하세요.