ASP.NET Web API의 라우팅 및 작업 선택

이 문서에서는 ASP.NET Web API 컨트롤러의 특정 작업에 HTTP 요청을 라우팅하는 방법을 설명합니다.

참고

라우팅에 대한 개략적인 개요는 ASP.NET Web API 라우팅을 참조하세요.

이 문서에서는 라우팅 프로세스의 세부 정보를 확인합니다. Web API 프로젝트를 만들고 일부 요청이 예상대로 라우팅되지 않는 것을 발견하면 이 문서가 도움이 될 것입니다.

라우팅에는 세 가지 기본 단계가 있습니다.

  1. URI를 경로 템플릿과 일치합니다.
  2. 컨트롤러 선택
  3. 작업 선택

프로세스의 일부 부분을 사용자 지정 동작으로 바꿀 수 있습니다. 이 문서에서는 기본 동작에 대해 설명합니다. 마지막에 동작을 사용자 지정할 수 있는 위치를 확인합니다.

경로 템플릿

경로 템플릿은 URI 경로와 비슷하지만 중괄호로 표시된 자리 표시자 값을 가질 수 있습니다.

"api/{controller}/public/{category}/{id}"

경로를 만들 때 자리 표시자의 일부 또는 전부에 대한 기본값을 제공할 수 있습니다.

defaults: new { category = "all" }

URI 세그먼트가 자리 표시자와 일치하는 방법을 제한하는 제약 조건을 제공할 수도 있습니다.

constraints: new { id = @"\d+" }   // Only matches if "id" is one or more digits.

프레임워크는 템플릿에 대한 URI 경로의 세그먼트와 일치하려고 합니다. 템플릿의 리터럴은 정확히 일치해야 합니다. 자리 표시자는 제약 조건을 지정하지 않는 한 모든 값과 일치합니다. 프레임워크는 호스트 이름 또는 쿼리 매개 변수와 같은 URI의 다른 부분과 일치하지 않습니다. 프레임워크는 경로 테이블에서 URI와 일치하는 첫 번째 경로를 선택합니다.

두 개의 특수 자리 표시자가 있습니다. "{controller}" 및 "{action}".

  • "{controller}"는 컨트롤러의 이름을 제공합니다.
  • "{action}"은 작업의 이름을 제공합니다. Web API에서 일반적인 규칙은 "{action}"을 생략하는 것입니다.

기본값

기본값을 제공하는 경우 경로는 해당 세그먼트가 누락된 URI와 일치합니다. 예:

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{category}",
    defaults: new { category = "all" }
);

URI http://localhost/api/products/allhttp://localhost/api/products 는 이전 경로와 일치합니다. 후자의 URI에서 누락된 {category} 세그먼트에는 기본값 all이 할당됩니다.

경로 사전

프레임워크가 URI에 대한 일치 항목을 찾은 경우 각 자리 표시자에 대한 값을 포함하는 사전을 만듭니다. 키는 중괄호를 포함하지 않는 자리 표시자 이름입니다. 값은 URI 경로 또는 기본값에서 가져옵니다. 사전은 IHttpRouteData 개체에 저장됩니다.

이 경로 일치 단계에서 특수 "{controller}" 및 "{action}" 자리 표시자는 다른 자리 표시자처럼 처리됩니다. 다른 값과 함께 사전에 저장되기만 하면됩니다.

기본값은 RouteParameter.Optional 특수 값을 가질 수 있습니다. 자리 표시자에 이 값이 할당되면 경로 사전에 값이 추가되지 않습니다. 예:

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{category}/{id}",
    defaults: new { category = "all", id = RouteParameter.Optional }
);

URI 경로 "api/products"의 경우 경로 사전에는 다음이 포함됩니다.

  • 컨트롤러: "제품"
  • category: "all"

그러나 "api/products/toys/123"의 경우 경로 사전에는 다음이 포함됩니다.

  • 컨트롤러: "제품"
  • 범주: "장난감"
  • id: "123"

기본값은 경로 템플릿의 아무 곳에도 표시되지 않는 값을 포함할 수도 있습니다. 경로가 일치하면 해당 값이 사전에 저장됩니다. 예:

routes.MapHttpRoute(
    name: "Root",
    routeTemplate: "api/root/{id}",
    defaults: new { controller = "customers", id = RouteParameter.Optional }
);

URI 경로가 "api/root/8"이면 사전에 다음 두 값이 포함됩니다.

  • 컨트롤러: "고객"
  • id: "8"

컨트롤러 선택

컨트롤러 선택은 IHttpControllerSelector.SelectController 메서드에 의해 처리됩니다. 이 메서드는 HttpRequestMessage instance 사용하고 HttpControllerDescriptor를 반환합니다. 기본 구현은 DefaultHttpControllerSelector 클래스에서 제공됩니다. 이 클래스는 간단한 알고리즘을 사용합니다.

  1. 경로 사전에서 키 "컨트롤러"를 찾습니다.
  2. 이 키의 값을 가져와서 "Controller" 문자열을 추가하여 컨트롤러 형식 이름을 가져옵니다.
  3. 이 형식 이름을 가진 Web API 컨트롤러를 찾습니다.

예를 들어 경로 사전에 키-값 쌍 "controller" = "products"가 포함된 경우 컨트롤러 유형은 "ProductsController"입니다. 일치하는 형식이 없거나 일치하는 형식이 여러 개 없는 경우 프레임워크는 클라이언트에 오류를 반환합니다.

3단계의 경우 DefaultHttpControllerSelectorIHttpControllerTypeResolver 인터페이스를 사용하여 Web API 컨트롤러 형식 목록을 가져옵니다. IHttpControllerTypeResolver의 기본 구현은 (a) IHttpController를 구현하는 모든 공용 클래스를 반환하고, (b)는 추상적이지 않으며 (c)에는 "Controller"로 끝나는 이름이 있습니다.

작업 선택

컨트롤러를 선택한 후 프레임워크는 IHttpActionSelector.SelectAction 메서드를 호출하여 작업을 선택합니다. 이 메서드는 HttpControllerContext 를 사용하고 HttpActionDescriptor를 반환합니다.

기본 구현은 ApiControllerActionSelector 클래스에서 제공됩니다. 작업을 선택하려면 다음을 확인합니다.

  • 요청의 HTTP 메서드입니다.
  • 경로 템플릿의 "{action}" 자리 표시자(있는 경우)입니다.
  • 컨트롤러에 대한 작업의 매개 변수입니다.

선택 알고리즘을 살펴보기 전에 컨트롤러 작업에 대한 몇 가지 사항을 이해해야 합니다.

컨트롤러에서 "작업"으로 간주되는 메서드는 무엇입니까? 작업을 선택할 때 프레임워크는 컨트롤러의 공용 instance 메서드만 살펴봅니다. 또한 ApiController 클래스에서 상속된 "특수 이름" 메서드(생성자, 이벤트, 연산자 오버로드 등)와 메서드를 제외합니다.

HTTP 메서드. 프레임워크는 다음과 같이 결정되는 요청의 HTTP 메서드와 일치하는 작업만 선택합니다.

  1. AcceptVerbs, HttpDelete, HttpGet, HttpHead, HttpOptions, HttpPatch, HttpPost 또는 HttpPut 특성을 사용하여 HTTP 메서드를 지정할 수 있습니다.
  2. 그렇지 않으면 컨트롤러 메서드의 이름이 "Get", "Post", "Put", "Delete", "Head", "Options" 또는 "Patch"로 시작하는 경우 규칙에 따라 작업이 해당 HTTP 메서드를 지원합니다.
  3. 위의 항목이 없는 경우 메서드는 POST를 지원합니다.

매개 변수 바인딩. 매개 변수 바인딩은 Web API가 매개 변수에 대한 값을 만드는 방법입니다. 매개 변수 바인딩에 대한 기본 규칙은 다음과 같습니다.

  • 간단한 형식은 URI에서 가져옵니다.
  • 복합 형식은 요청 본문에서 가져옵니다.

단순 형식에는 모든 .NET Framework 기본 형식DateTime, Decimal, Guid, StringTimeSpan이 포함됩니다. 각 작업에 대해 최대 하나의 매개 변수가 요청 본문을 읽을 수 있습니다.

참고

기본 바인딩 규칙을 재정의할 수 있습니다. 내부 WebAPI 매개 변수 바인딩을 참조하세요.

해당 배경을 사용하여 작업 선택 알고리즘은 다음과 같습니다.

  1. HTTP 요청 메서드와 일치하는 컨트롤러의 모든 작업 목록을 만듭니다.

  2. 경로 사전에 "action" 항목이 있는 경우 이름이 이 값과 일치하지 않는 작업을 제거합니다.

  3. 다음과 같이 작업 매개 변수를 URI와 일치시키려고 합니다.

    1. 각 작업에 대해 바인딩이 URI에서 매개 변수를 가져오는 간단한 형식의 매개 변수 목록을 가져옵니다. 선택적 매개 변수를 제외합니다.
    2. 이 목록에서 경로 사전 또는 URI 쿼리 문자열에서 각 매개 변수 이름에 대한 일치 항목을 찾습니다. 일치 항목은 대/소문자를 구분하지 않으며 매개 변수 순서에 의존하지 않습니다.
    3. 목록의 모든 매개 변수가 URI에서 일치하는 작업을 선택합니다.
    4. 하나의 작업이 이러한 조건을 충족하는 경우 매개 변수 일치 항목이 가장 많은 작업을 선택합니다.
  4. [NonAction] 특성을 사용하여 작업을 무시합니다.

3단계가 가장 혼란스러울 수 있습니다. 기본 개념은 매개 변수가 URI, 요청 본문 또는 사용자 지정 바인딩에서 해당 값을 가져올 수 있다는 것입니다. URI에서 제공되는 매개 변수의 경우 경로(경로 사전을 통해) 또는 쿼리 문자열에서 URI에 해당 매개 변수에 대한 값이 실제로 포함되어 있는지 확인하려고 합니다.

다음 작업을 예로 들 수 있습니다.

public void Get(int id)

id 매개 변수는 URI에 바인딩됩니다. 따라서 이 작업은 경로 사전 또는 쿼리 문자열에서 "id" 값이 포함된 URI와만 일치할 수 있습니다.

선택적 매개 변수는 선택 사항이므로 예외입니다. 선택적 매개 변수의 경우 바인딩이 URI에서 값을 가져올 수 없는 경우 괜찮습니다.

복합 형식은 다른 이유로 인해 예외입니다. 복합 형식은 사용자 지정 바인딩을 통해서만 URI에 바인딩할 수 있습니다. 그러나 이 경우 프레임워크는 매개 변수가 특정 URI에 바인딩되는지 여부를 미리 알 수 없습니다. 알아보려면 바인딩을 호출해야 합니다. 선택 알고리즘의 목표는 바인딩을 호출하기 전에 정적 설명에서 작업을 선택하는 것입니다. 따라서 복합 형식은 일치하는 알고리즘에서 제외됩니다.

작업을 선택하면 모든 매개 변수 바인딩이 호출됩니다.

요약:

  • 작업은 요청의 HTTP 메서드와 일치해야 합니다.
  • 작업 이름이 있는 경우 경로 사전의 "action" 항목과 일치해야 합니다.
  • 작업의 모든 매개 변수에 대해 매개 변수가 URI에서 가져온 경우 매개 변수 이름은 경로 사전 또는 URI 쿼리 문자열에서 찾아야 합니다. (복합 형식의 선택적 매개 변수 및 매개 변수는 제외됩니다.)
  • 가장 많은 수의 매개 변수를 일치시키려고 합니다. 가장 일치하는 항목은 매개 변수가 없는 메서드일 수 있습니다.

확장 예제

경로:

routes.MapHttpRoute(
    name: "ApiRoot",
    routeTemplate: "api/root/{id}",
    defaults: new { controller = "products", id = RouteParameter.Optional }
);
routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

컨트롤러:

public class ProductsController : ApiController
{
    public IEnumerable<Product> GetAll() {}
    public Product GetById(int id, double version = 1.0) {}
    [HttpGet]
    public void FindProductsByName(string name) {}
    public void Post(Product value) {}
    public void Put(int id, Product value) {}
}

HTTP 요청:

GET http://localhost:34701/api/products/1?version=1.5&details=1

경로 일치

URI는 "DefaultApi"라는 경로와 일치합니다. 경로 사전에는 다음 항목이 포함됩니다.

  • 컨트롤러: "제품"
  • id: "1"

경로 사전에는 쿼리 문자열 매개 변수인 "version" 및 "details"가 포함되지 않지만 작업 선택 중에는 계속 고려됩니다.

컨트롤러 선택

경로 사전의 "컨트롤러" 항목에서 컨트롤러 유형은 입니다 ProductsController.

작업 선택

HTTP 요청은 GET 요청입니다. GET을 지원하는 컨트롤러 작업은 , GetByIdFindProductsByName입니다GetAll. 경로 사전에는 "action"에 대한 항목이 없으므로 작업 이름과 일치시킬 필요가 없습니다.

다음으로 GET 작업만 살펴보면서 작업에 대한 매개 변수 이름을 일치시키려고 합니다.

작업 일치시킬 매개 변수
GetAll 없음
GetById "id"
FindProductsByName "name"

의 버전 매개 변수는 선택적 매개 변수 GetById 이므로 고려되지 않습니다.

메서드는 GetAll 사소하게 일치합니다. GetById 경로 사전에 "id"가 포함되어 있으므로 메서드도 일치합니다. 메서드가 FindProductsByName 일치하지 않습니다.

메서드는 GetById 하나의 매개 변수와 일치하고 에 대한 GetAll매개 변수가 없으므로 우선합니다. 메서드는 다음 매개 변수 값으로 호출됩니다.

  • id = 1
  • version = 1.5

버전이 선택 알고리즘에서 사용되지 않았더라도 매개 변수 값은 URI 쿼리 문자열에서 가져옵니다.

확장점

Web API는 라우팅 프로세스의 일부 부분에 대한 확장 지점을 제공합니다.

인터페이스 설명
IHttpControllerSelector 컨트롤러를 선택합니다.
IHttpControllerTypeResolver 컨트롤러 유형의 목록을 가져옵니다. DefaultHttpControllerSelector는 이 목록에서 컨트롤러 유형을 선택합니다.
IAssembliesResolver 프로젝트 어셈블리 목록을 가져옵니다. IHttpControllerTypeResolver 인터페이스는 이 목록을 사용하여 컨트롤러 형식을 찾습니다.
IHttpControllerActivator 새 컨트롤러 인스턴스를 만듭니다.
IHttpActionSelector 작업을 선택합니다.
IHttpActionInvoker 작업을 호출합니다.

이러한 인터페이스에 대한 고유한 구현을 제공하려면 HttpConfiguration 개체의 Services 컬렉션을 사용합니다.

var config = GlobalConfiguration.Configuration;
config.Services.Replace(typeof(IHttpControllerSelector), new MyControllerSelector(config));