다음을 통해 공유


ASP.NET Core의 모델 바인딩

참고 항목

이 문서의 최신 버전은 아닙니다. 현재 릴리스는 이 문서의 .NET 9 버전을 참조 하세요.

Warning

이 버전의 ASP.NET Core는 더 이상 지원되지 않습니다. 자세한 내용은 .NET 및 .NET Core 지원 정책을 참조 하세요. 현재 릴리스는 이 문서의 .NET 8 버전을 참조 하세요.

Important

이 정보는 상업적으로 출시되기 전에 실질적으로 수정될 수 있는 시험판 제품과 관련이 있습니다. Microsoft는 여기에 제공된 정보에 대해 어떠한 명시적, 또는 묵시적인 보증을 하지 않습니다.

현재 릴리스는 이 문서의 .NET 9 버전을 참조 하세요.

이 문서는 모델 바인딩이 무엇인지, 작동 방법 및 해당 동작을 사용자 지정하는 방법을 설명합니다.

모델 바인딩이란

컨트롤러 및 Razor Pages는 HTTP 요청에서 제공되는 데이터를 사용하여 작동합니다. 예를 들어 경로 데이터는 레코드 키를 제공할 수 있으며, 게시된 양식 필드는 모델의 속성에 대한 값을 제공할 수 있습니다. 이러한 각 값을 검색하고 문자열에서 .NET 형식으로 변환하도록 코드를 작성하는 것은 번거롭고 오류가 발생하기 쉽습니다. 모델 바인딩은 이 프로세스를 자동화합니다. 모델 바인딩 시스템:

  • 경로 데이터, 양식 필드 및 쿼리 문자열과 같은 다양한 원본의 데이터를 검색합니다.
  • 메서드 매개 변수 및 공용 속성에서 컨트롤러 및 Razor Pages에 데이터를 제공합니다.
  • 문자열 데이터를 .NET 형식으로 변환합니다.
  • 복합 형식의 속성을 업데이트합니다.

예시

다음 작업 메서드를 사용합니다.

[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)

앱은 이 URL로 요청을 수신합니다.

https://contoso.com/api/pets/2?DogsOnly=true

모델 바인딩은 라우팅 시스템이 작업 메서드를 선택한 후 다음 단계를 통해 진행됩니다.

  • GetById의 첫 번째 매개 변수, id라는 정수를 찾습니다.
  • HTTP 요청에서 사용 가능한 원본을 찾고 경로 데이터에서 id = "2"를 찾습니다.
  • 문자열 "2"를 정수 2로 변환합니다.
  • GetById의 다음 매개 변수, dogsOnly라는 부울을 찾습니다.
  • 원본을 찾고 쿼리 문자열에서 "DogsOnly=true"를 찾습니다. 이름 일치는 대/소문자를 구분하지 않습니다.
  • 문자열 "true"를 부울 true로 변환합니다.

그런 다음, 프레임워크는 id 매개 변수에 대해 2를 dogsOnly 매개 변수에 대해 true를 전달하는 GetById 메서드를 호출합니다.

이전 예제에서 모델 바인딩 대상은 간단한 형식인 메서드 매개 변수입니다. 대상은 복합 형식의 속성일 수도 있습니다. 각 속성이 성공적으로 바인딩된 후 모델 유효성 검사가 해당 속성에 대해 발생합니다. 모델에 바인딩되는 데이터의 레코드 및 모든 바인딩 또는 유효성 검사 오류는 ControllerBase.ModelState 또는 PageModel.ModelState에 저장됩니다. 이 프로세스가 성공되었는지 확인하기 위해 앱은 ModelState.IsValid 플래그를 확인합니다.

대상

모델 바인딩은 다음 종류의 대상에 대한 값을 찾으려고 합니다.

  • 요청이 라우팅되는 컨트롤러 작업 메서드의 매개 변수
  • 요청이 라우팅되는 Razor Pages 처리기 메서드의 매개 변수
  • 특성으로 지정되는 경우 컨트롤러 또는 PageModel 클래스의 공용 속성

[BindProperty] 특성

모델 바인딩이 해당 속성을 대상으로 하도록 컨트롤러의 공용 속성 또는 PageModel 클래스에 적용할 수 있습니다.

public class EditModel : PageModel
{
    [BindProperty]
    public Instructor? Instructor { get; set; }

    // ...
}

[BindProperties] 특성

모델 바인딩이 클래스의 모든 공용 속성을 대상으로 하도록 컨트롤러 또는 PageModel 클래스에 적용할 수 있습니다.

[BindProperties]
public class CreateModel : PageModel
{
    public Instructor? Instructor { get; set; }

    // ...
}

HTTP GET 요청에 대한 모델 바인딩

기본적으로 속성은 HTTP GET 요청에 대해 바인딩되지 않습니다. 일반적으로 GET 요청에 대해 필요한 것은 레코드 ID 매개 변수입니다. 레코드 ID는 데이터베이스에 있는 항목을 찾는 데 사용됩니다. 따라서 모델의 인스턴스를 포함하는 속성을 바인딩할 필요가 없습니다. 속성을 GET 요청의 데이터에 바인딩하려는 시나리오에서 SupportsGet 속성을 true로 설정합니다.

[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }

단순 및 복합 형식 모델 바인딩

모델 바인딩은 작업을 수행하는 형식에 대한 특정 정의를 사용합니다. 단순 형식은 사용하거나 TryParse 메서드를 사용하여 TypeConverter 단일 문자열에서 변환됩니다. 복합 형식은 여러 입력 값에서 변환됩니다. 프레임워크는 TypeConverter 또는 TryParse 존재 여부에 따라 차이를 결정합니다. 형식 변환기를 만들거나 외부 리소스 또는 여러 입력이 필요하지 않은 SomeType 변환에 string을 위한 TryParse를 사용하는 것이 좋습니다.

원본

기본적으로 모델 바인딩은 HTTP 요청의 다음 원본에서 키-값 쌍의 양식으로 데이터를 가져옵니다.

  1. 양식 필드
  2. 요청 본문([ApiController] 특성이 있는 컨트롤러의 경우)
  3. 경로 데이터
  4. 쿼리 문자열 매개 변수
  5. 업로드된 파일

각 대상 매개 변수 또는 속성의 경우, 소스가 이 목록에 표시된 순서대로 검사됩니다. 몇 가지 예외도 있습니다.

  • 경로 데이터 및 쿼리 문자열 값은 간단한 형식에만 사용됩니다.
  • 업로드된 파일은 IFormFile 또는 IEnumerable<IFormFile>을 구현하는 대상 유형에만 바인딩됩니다.

기본 소스가 올바르지 않으면 다음 특성 중 하나를 사용하여 소스를 지정합니다.

  • [FromQuery] - 쿼리 문자열에서 값을 가져옵니다.
  • [FromRoute] - 경로 데이터에서 값을 가져옵니다.
  • [FromForm] - 게시된 양식 필드에서 값을 가져옵니다.
  • [FromBody] - 요청 본문에서 값을 가져옵니다.
  • [FromHeader] - HTTP 헤더에서 값을 가져옵니다.

이러한 특성:

  • 다음 예제와 같이 모델 클래스가 아닌 모델 속성에 개별적으로 추가됩니다.

    public class Instructor
    {
        public int Id { get; set; }
    
        [FromQuery(Name = "Note")]
        public string? NoteFromQueryString { get; set; }
    
        // ...
    }
    
  • 필요에 따라 생성자에서 모델 이름 값을 허용합니다. 이 옵션은 속성 이름이 요청의 값과 일치하지 않는 경우에 제공됩니다. 예를 들어 요청의 값은 다음 예제와 같이 해당 이름에 하이픈이 있는 헤더일 수 있습니다.

    public void OnGet([FromHeader(Name = "Accept-Language")] string language)
    

[FromBody] 특성

매개 변수에 [FromBody] 특성을 적용하여 HTTP 요청의 본문에서 해당 속성을 채웁니다. ASP.NET Core 런타임은 본문을 읽을 책임을 입력 포맷터에 위임합니다. 입력 포맷터는 이 문서의 뒷부분에 설명되어 있습니다.

[FromBody]이(가) 복합 형식 매개 변수에 적용되는 경우 해당 속성에 적용된 모든 바인딩 소스 특성은 무시됩니다. 예를 들어, 다음 Create 작업은 해당 pet 매개 변수가 본문에서 채워지도록 지정합니다.

public ActionResult<Pet> Create([FromBody] Pet pet)

Pet 클래스는 Breed 속성이 쿼리 문자열 매개 변수에서 채워지도록 지정합니다.

public class Pet
{
    public string Name { get; set; } = null!;

    [FromQuery] // Attribute is ignored.
    public string Breed { get; set; } = null!;
}

앞의 예에서:

  • [FromQuery] 특성은 무시됩니다.
  • Breed 속성은 쿼리 문자열 매개 변수에서 채워지지 않습니다.

입력 포맷터는 본문만 읽고 바인딩 소스 특성은 인식하지 않습니다. 본문에 적절한 값이 있는 경우 해당 값은 Breed 속성을 채우는 데 사용됩니다.

작업 메서드당 둘 이상의 매개 변수에 [FromBody]를 적용하지 마십시오. 입력 포맷터에서 요청 스트림을 읽으면 더 이상 다른 [FromBody] 매개 변수를 바인딩하기 위해 다시 읽을 수 없습니다.

추가 원본

원본 데이터는 값 공급 기업에 의해 모델 바인딩 시스템에 제공됩니다. 다른 원본에서 모델 바인딩에 대한 데이터를 가져오는 사용자 지정 값 공급 기업을 작성 및 등록할 수 있습니다. 예를 들어 쿠키 또는 세션 상태의 데이터를 원할 수 있습니다. 새 원본에서 데이터를 가져오려면 다음을 수행합니다.

  • IValueProvider를 구현하는 클래스를 만듭니다.
  • IValueProviderFactory를 구현하는 클래스를 만듭니다.
  • Program.cs에서 팩터리 클래스를 등록합니다.

이 샘플에는 쿠키에서 값을 가져오는 값 공급자팩터리 예제가 포함되어 있습니다. Program.cs에서 사용자 지정 값 공급자 센터를 등록합니다.

builder.Services.AddControllers(options =>
{
    options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});

이전 코드는 모든 기본 제공 값 공급자 이후에 사용자 지정 값 공급자를 배치합니다. 목록에서 첫 번째로 지정하려면 Add 대신에 Insert(0, new CookieValueProviderFactory())를 호출합니다.

모델 속성에 대한 원본 없음

기본적으로 모델 속성에 대한 값이 없으면 모델 상태 오류가 생성되지 않습니다. 속성은 Null 또는 기본값으로 설정됩니다.

  • Nullable 단순 형식은 null로 설정됩니다.
  • Null을 허용하지 않는 값 형식은 default(T)로 설정됩니다. 예를 들어 매개 변수 int id는 0으로 설정됩니다.
  • 복합 형식의 경우 모델 바인딩은 속성을 설정하지 않고 기본 생성자를 사용하여 인스턴스를 만듭니다.
  • 배열은 byte[] 배열이 null로 설정되는 점을 제외하고 Array.Empty<T>()로 설정됩니다.

모델 속성에 대한 양식 필드에 아무것도 없을 때 모델 상태가 무효화되어야 하는 경우 [BindRequired] 속성을 사용합니다.

[BindRequired] 동작은 요청 본문의 JSON 또는 XML 데이터가 아니라 게시된 양식 데이터의 모델 바인딩에 적용됩니다. 요청 본문 데이터는 입력 포맷터에서 처리됩니다.

형식 변환 오류

원본이 있지만 대상 형식으로 변환될 수 없는 경우 모델 상태는 잘못된 것으로 플래그가 지정됩니다. 이전 섹션에서 설명한 것처럼 대상 매개 변수 또는 속성은 Null 또는 기본값으로 설정됩니다.

[ApiController] 특성이 있는 API 컨트롤러에서 잘못된 모델 상태로 자동 HTTP 400 응답이 발생합니다.

Razor Page에서 페이지를 오류 메시지와 함께 다시 표시합니다.

public IActionResult OnPost()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    // ...

    return RedirectToPage("./Index");
}

이전 코드로 페이지를 다시 표시하면 양식 필드에 유효하지 않은 입력이 표시되지 않습니다. 이는 모델 속성이 Null 또는 기본값으로 설정되었기 때문입니다. 잘못된 입력은 오류 메시지에 표시됩니다. 양식 필드에서 잘못된 데이터를 다시 표시하려면 모델 속성을 문자열로 만들고 데이터 변환을 수동으로 수행하는 것이 좋습니다.

형식 변환 오류를 모델 상태 오류로 만들려 하지 않는 경우 동일한 전략을 권장합니다. 이 경우 모델 속성을 문자열로 만듭니다.

단순 형식

단순 및 복합 형식에 대한 설명은 단순 및 복합 형식 모델 바인딩을 참조하세요.

모델 바인더에서 원본 문자열을 변환할 수 있는 단순 형식은 다음을 포함합니다.

IParsable<T>.TryParse를 사용하여 바인딩

IParsable<TSelf>.TryParse API는 바인딩 컨트롤러 작업 매개 변수 값을 지원합니다.

public static bool TryParse (string? s, IFormatProvider? provider, out TSelf result);

다음 DateRange 클래스는 IParsable<TSelf>을 구현하여 날짜 범위 바인딩을 지원합니다.

public class DateRange : IParsable<DateRange>
{
    public DateOnly? From { get; init; }
    public DateOnly? To { get; init; }

    public static DateRange Parse(string value, IFormatProvider? provider)
    {
        if (!TryParse(value, provider, out var result))
        {
           throw new ArgumentException("Could not parse supplied value.", nameof(value));
        }

        return result;
    }

    public static bool TryParse(string? value,
                                IFormatProvider? provider, out DateRange dateRange)
    {
        var segments = value?.Split(',', StringSplitOptions.RemoveEmptyEntries 
                                       | StringSplitOptions.TrimEntries);

        if (segments?.Length == 2
            && DateOnly.TryParse(segments[0], provider, out var fromDate)
            && DateOnly.TryParse(segments[1], provider, out var toDate))
        {
            dateRange = new DateRange { From = fromDate, To = toDate };
            return true;
        }

        dateRange = new DateRange { From = default, To = default };
        return false;
    }
}

앞의 코드가 하는 역할은 다음과 같습니다.

  • 두 날짜를 나타내는 문자열을 DateRange 개체로 변환합니다.
  • 모델 바인더는 IParsable<TSelf>.TryParse 메서드를 사용하여 DateRange를 바인딩합니다.

다음 컨트롤러 작업은 DateRange 클래스를 사용하여 날짜 범위를 바인딩합니다.

// GET /WeatherForecast/ByRange?range=7/24/2022,07/26/2022
public IActionResult ByRange([FromQuery] DateRange range)
{
    if (!ModelState.IsValid)
        return View("Error", ModelState.Values.SelectMany(v => v.Errors));

    var weatherForecasts = Enumerable
        .Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .Where(wf => DateOnly.FromDateTime(wf.Date) >= range.From
                     && DateOnly.FromDateTime(wf.Date) <= range.To)
        .Select(wf => new WeatherForecastViewModel
        {
            Date = wf.Date.ToString("d"),
            TemperatureC = wf.TemperatureC,
            TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
            Summary = wf.Summary
        });

    return View("Index", weatherForecasts);
}

다음 Locale 클래스는 IParsable<TSelf>을 구현하여 CultureInfo 바인딩을 지원합니다.

public class Locale : CultureInfo, IParsable<Locale>
{
    public Locale(string culture) : base(culture)
    {
    }

    public static Locale Parse(string value, IFormatProvider? provider)
    {
        if (!TryParse(value, provider, out var result))
        {
           throw new ArgumentException("Could not parse supplied value.", nameof(value));
        }

        return result;
    }

    public static bool TryParse([NotNullWhen(true)] string? value,
                                IFormatProvider? provider, out Locale locale)
    {
        if (value is null)
        {
            locale = new Locale(CurrentCulture.Name);
            return false;
        }
        
        try
        {
            locale = new Locale(value);
            return true;
        }
        catch (CultureNotFoundException)
        {
            locale = new Locale(CurrentCulture.Name);
            return false;
        }
    }
}

다음 컨트롤러 작업은 Locale 클래스를 사용하여 CultureInfo 문자열을 바인딩합니다.

// GET /en-GB/WeatherForecast
public IActionResult Index([FromRoute] Locale locale)
{
    var weatherForecasts = Enumerable
        .Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .Select(wf => new WeatherForecastViewModel
        {
            Date = wf.Date.ToString("d", locale),
            TemperatureC = wf.TemperatureC,
            TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
            Summary = wf.Summary
        });

    return View(weatherForecasts);
}

다음 컨트롤러 작업은 DateRangeLocale 클래스를 사용하여 날짜 범위를 CultureInfo로 바인딩합니다.

// GET /af-ZA/WeatherForecast/RangeByLocale?range=2022-07-24,2022-07-29
public IActionResult RangeByLocale([FromRoute] Locale locale, [FromQuery] string range)
{
    if (!ModelState.IsValid)
        return View("Error", ModelState.Values.SelectMany(v => v.Errors));

    if (!DateRange.TryParse(range, locale, out DateRange rangeResult))
    {
        ModelState.TryAddModelError(nameof(range),
            $"Invalid date range: {range} for locale {locale.DisplayName}");

        return View("Error", ModelState.Values.SelectMany(v => v.Errors));
    }

    var weatherForecasts = Enumerable
        .Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .Where(wf => DateOnly.FromDateTime(wf.Date) >= rangeResult.From
                     && DateOnly.FromDateTime(wf.Date) <= rangeResult.To)
        .Select(wf => new WeatherForecastViewModel
        {
            Date = wf.Date.ToString("d", locale),
            TemperatureC = wf.TemperatureC,
            TemperatureF = 32 + (int) (wf.TemperatureC / 0.5556),
            Summary = wf.Summary
        });

    return View("Index", weatherForecasts);
}

GitHub의 API 샘플 앱은 API 컨트롤러에 대한 이전 샘플을 보여줍니다.

TryParse를 사용하여 바인딩

TryParse API는 바인딩 컨트롤러 작업 매개 변수 값을 지원합니다.

public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);

IParsable<T>.TryParseTryParse와 달리 리플렉션에 의존하지 않기 때문에 매개 변수 바인딩에 권장되는 접근 방식입니다.

다음 DateRangeTP 클래스는 TryParse를 구현합니다.

public class DateRangeTP
{
    public DateOnly? From { get; }
    public DateOnly? To { get; }

    public DateRangeTP(string from, string to)
    {
        if (string.IsNullOrEmpty(from))
            throw new ArgumentNullException(nameof(from));
        if (string.IsNullOrEmpty(to))
            throw new ArgumentNullException(nameof(to));

        From = DateOnly.Parse(from);
        To = DateOnly.Parse(to);
    }

    public static bool TryParse(string? value, out DateRangeTP? result)
    {
        var range = value?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
        if (range?.Length != 2)
        {
            result = default;
            return false;
        }

        result = new DateRangeTP(range[0], range[1]);
        return true;
    }
}

다음 컨트롤러 작업은 DateRangeTP 클래스를 사용하여 날짜 범위를 바인딩합니다.

// GET /WeatherForecast/ByRangeTP?range=7/24/2022,07/26/2022
public IActionResult ByRangeTP([FromQuery] DateRangeTP range)
{
    if (!ModelState.IsValid)
        return View("Error", ModelState.Values.SelectMany(v => v.Errors));

    var weatherForecasts = Enumerable
        .Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .Where(wf => DateOnly.FromDateTime(wf.Date) >= range.From
                     && DateOnly.FromDateTime(wf.Date) <= range.To)
        .Select(wf => new WeatherForecastViewModel
        {
            Date = wf.Date.ToString("d"),
            TemperatureC = wf.TemperatureC,
            TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
            Summary = wf.Summary
        });

    return View("Index", weatherForecasts);
}

복합 형식

복합 형식에 공용 기본 생성자와 바인딩할 공용 쓰기 가능 속성이 있어야 합니다. 모델 바인딩이 발생하면 클래스는 공용 기본 생성자를 사용하여 인스턴스화됩니다.

복합 형식 의 각 속성에 대해 모델 바인딩은 이름 패턴 prefix.property_name 원본을 살펴봅니다. 아무것도 없는 경우 접두사 없이 property_name만을 찾습니다. 접두사를 사용할지 여부는 속성별로 결정되는 것이 아닙니다. 예를 들어 메서드 OnGet(Instructor instructor) 바인딩된 ?Instructor.Id=100&Name=foo를 포함하는 쿼리의 경우 Instructor 형식의 결과 개체에는 다음이 포함됩니다.

  • Id(이)가 100(으)로 설정됩니다.
  • Name(이)가 null(으)로 설정됩니다. 이전 쿼리 매개 변수에서 Instructor.Id가 사용되었기 때문에 모델 바인딩에는 Instructor.Name이 필요합니다.

매개 변수에 대한 바인딩의 경우 접두사는 매개 변수 이름입니다. PageModel 공용 속성에 대한 바인딩의 경우 접두사는 공용 속성 이름입니다. 일부 특성에는 매개 변수의 기본 사용 또는 속성 이름을 재정의할 수 있도록 하는 Prefix 속성이 있습니다.

예를 들어 복합 형식이 다음 Instructor 클래스라고 가정합니다.

public class Instructor
{
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
}

접두사 = 매개 변수 이름

바인딩될 모델이 instructorToUpdate라는 매개 변수인 경우:

public IActionResult OnPost(int? id, Instructor instructorToUpdate)

모델 바인딩은 키 instructorToUpdate.ID에 대한 원본을 찾아 시작합니다. 없는 경우 접두사 없이 ID를 찾습니다.

접두사 = 속성 이름

바인딩될 모델이 컨트롤러 또는 PageModel 클래스의 Instructor라는 속성인 경우:

[BindProperty]
public Instructor Instructor { get; set; }

모델 바인딩은 키 Instructor.ID에 대한 원본을 찾아 시작합니다. 없는 경우 접두사 없이 ID를 찾습니다.

사용자 지정 접두사

바인딩될 모델이 instructorToUpdate라는 매개 변수이고 Bind 특성이 접두사로 Instructor를 지정하는 경우:

public IActionResult OnPost(
    int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)

모델 바인딩은 키 Instructor.ID에 대한 원본을 찾아 시작합니다. 없는 경우 접두사 없이 ID를 찾습니다.

복합 형식 대상에 대한 특성

여러 기본 제공 특성은 복합 형식의 모델 바인딩을 제어할 수 있습니다.

Warning

이러한 특성은 게시된 양식 데이터가 값의 원본일 때 모델 바인딩에 영향을 줍니다. 게시된 JSON 및 XML 요청 본문을 처리하는 입력 포맷터에는 영향을 주지 않습니다. 입력 포맷터는 이 문서의 뒷부분에 설명되어 있습니다.

[Bind] 특성

클래스 또는 메서드 매개 변수에 적용될 수 있습니다. 모델 바인딩에 포함되어야 하는 모델의 속성을 지정합니다. [Bind]는 입력 포맷터에 영향을 주지 않습니다.

다음 예제에서 모든 처리기 또는 작업 메서드가 호출될 때 지정된 속성의 Instructor 모델만 바인딩됩니다.

[Bind("LastName,FirstMidName,HireDate")]
public class Instructor

다음 예제에서 OnPost 메서드가 호출될 때 지정된 속성의 Instructor 모델만 바인딩됩니다.

[HttpPost]
public IActionResult OnPost(
    [Bind("LastName,FirstMidName,HireDate")] Instructor instructor)

[Bind] 특성은 만들기 시나리오에서 초과 게시를 방지하는 데 사용될 수 있습니다. 제외된 속성은 변경되지 않은 채로 남겨지는 대신 Null 또는 기본값으로 설정되기 때문에 편집 시나리오에서 잘 작동하지 않습니다. 오버포스트 방지를 위해 특성 대신 [Bind] 보기 모델을 사용하는 것이 좋습니다. 자세한 내용은 초과 게시에 대한 보안 정보를 참조하세요.

[ModelBinder] 특성

ModelBinderAttribute는 형식, 속성 또는 매개 변수에 적용할 수 있습니다. 특정 인스턴스 또는 형식을 바인딩하는 데 사용되는 모델 바인더의 형식을 지정할 수 있습니다. 예시:

[HttpPost]
public IActionResult OnPost(
    [ModelBinder<MyInstructorModelBinder>] Instructor instructor)

[ModelBinder] 특성은 모델 바인딩될 때 속성 또는 매개 변수의 이름을 변경하는 데 사용할 수도 있습니다.

public class Instructor
{
    [ModelBinder(Name = "instructor_id")]
    public string Id { get; set; }

    // ...
}

[BindRequired] 특성

모델의 속성에 대한 바인딩이 발생할 수 없는 경우 모델 바인딩이 모델 상태 오류를 추가하도록 합니다. 예를 들어 다음과 같습니다.

public class InstructorBindRequired
{
    // ...

    [BindRequired]
    public DateTime HireDate { get; set; }
}

모델 유효성 검사에서 [Required] 특성의 설명을 참조하세요.

[BindNever] 특성

속성 또는 형식에 적용할 수 있습니다. 모델 바인딩이 모델의 속성을 설정하는 것을 방지합니다. 형식에 적용한 경우 모델 바인딩 시스템은 형식이 정의하는 모든 속성을 제외합니다. 예를 들어 다음과 같습니다.

public class InstructorBindNever
{
    [BindNever]
    public int Id { get; set; }

    // ...
}

컬렉션

단순 형식의 컬렉션인 대상의 경우 모델 바인딩은 parameter_name 또는 property_name에 대한 일치 항목을 찾습니다. 일치하는 항목이 없는 경우 접두사 없이 지원되는 양식 중 하나를 찾습니다. 예시:

  • 바인딩되는 매개 변수가 selectedCourses라는 배열이라고 가정합니다.

    public IActionResult OnPost(int? id, int[] selectedCourses)
    
  • 양식 또는 쿼리 문자열 데이터는 다음 형식 중 하나일 수 있습니다.

    selectedCourses=1050&selectedCourses=2000 
    
    selectedCourses[0]=1050&selectedCourses[1]=2000
    
    [0]=1050&[1]=2000
    
    selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
    
    [a]=1050&[b]=2000&index=a&index=b
    

    index 또는 Index라는 이름의 매개 변수 또는 속성이 컬렉션 값에 인접한 경우에는 바인딩하지 않습니다. 모델 바인딩은 index를 컬렉션의 인덱스로 사용하려고 시도하며 이로 인해 바인딩이 잘못될 수 있습니다. 다음 작업을 예로 들 수 있습니다.

    public IActionResult Post(string index, List<Product> products)
    

    이전 코드에서 index 쿼리 문자열 매개 변수는 index 메서드 매개 변수에 바인딩하며 제품 컬렉션을 바인딩하는 데도 사용합니다. index 매개 변수 이름을 변경하거나 모델 바인딩 특성을 사용하여 바인딩을 구성하면 다음 문제가 발생하지 않습니다.

    public IActionResult Post(string productIndex, List<Product> products)
    
  • 다음 형식은 양식 데이터에서만 지원됩니다.

    selectedCourses[]=1050&selectedCourses[]=2000
    
  • 앞의 모든 예제 형식의 경우 모델 바인딩은 두 항목의 배열을 selectedCourses 매개 변수에 전달합니다.

    • selectedCourses[0]=1050
    • selectedCourses[1]=2000

    아래 첨자 숫자(... [0] ... [1] ...)를 사용하는 데이터 형식은 0부터 시작하여 순차적으로 번호가 매겨지는지 확인해야 합니다. 아래 첨자 번호에 간격이 있는 경우 간격 뒤에 있는 모든 항목은 무시됩니다. 예를 들어 아래 첨자가 0과 1 대신 0과 2인 경우 두 번째 항목은 무시됩니다.

사전

Dictionary 대상의 경우 모델 바인딩은 parameter_name 또는 property_name에 대한 일치 항목을 찾습니다. 일치하는 항목이 없는 경우 접두사 없이 지원되는 양식 중 하나를 찾습니다. 예시:

  • 대상 매개 변수가 selectedCourses라는 Dictionary<int, string>라고 가정합니다.

    public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
    
  • 게시된 양식 또는 쿼리 문자열 데이터는 다음 예제 중 하나와 같을 수 있습니다.

    selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
    
    [1050]=Chemistry&selectedCourses[2000]=Economics
    
    selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry&
    selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
    
    [0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
    
  • 앞의 모든 예제 형식의 경우 모델 바인딩은 두 항목의 사전을 selectedCourses 매개 변수에 전달합니다.

    • selectedCourses["1050"]="Chemistry"
    • selectedCourses["2000"]="Economics"

생성자 바인딩 및 레코드 형식

모델 바인딩을 사용하려면 복합 형식에 매개 변수가 없는 생성자가 있어야 합니다. System.Text.JsonNewtonsoft.Json 기반 입력 포맷터는 모두 매개 변수가 없는 생성자가 없는 클래스의 역직렬화를 지원합니다.

레코드 형식은 네트워크를 통해 데이터를 간결하게 나타내는 좋은 방법입니다. ASP.NET Core는 단일 생성자를 사용하여 모델 바인딩과 레코드 형식의 유효성 검사를 지원합니다.

public record Person(
    [Required] string Name, [Range(0, 150)] int Age, [BindNever] int Id);

public class PersonController
{
    public IActionResult Index() => View();

    [HttpPost]
    public IActionResult Index(Person person)
    {
        // ...
    }
}

Person/Index.cshtml:

@model Person

<label>Name: <input asp-for="Name" /></label>
<br />
<label>Age: <input asp-for="Age" /></label>

레코드 형식의 유효성을 검사할 때 런타임에서는 속성이 아닌 매개 변수에 대한 바인딩 및 유효성 검사 메타데이터를 검색합니다.

프레임워크는 레코드 형식에 대한 바인딩 및 유효성 검사를 허용합니다.

public record Person([Required] string Name, [Range(0, 100)] int Age);

위의 작업을 수행하려면 형식은 다음과 같아야 합니다.

  • 레코드 형식이어야 합니다.
  • 공용 생성자가 하나만 있어야 합니다.
  • 이름 및 형식이 같은 속성을 포함하는 매개 변수를 포함합니다. 경우에 따라 이름이 달라져서는 안 됩니다.

매개 변수가 없는 생성자가 없는 POCO

매개 변수가 없는 생성자가 없는 POCO는 바인딩할 수 없습니다.

다음 코드에서는 형식에 매개 변수가 없는 생성자가 있어야 한다는 예외가 발생합니다.

public class Person(string Name)

public record Person([Required] string Name, [Range(0, 100)] int Age)
{
    public Person(string Name) : this (Name, 0);
}

수동으로 작성된 생성자를 사용하는 레코드 형식

기본 생성자가 작동하는 것처럼 보이는 수동으로 작성된 생성자를 사용하는 레코드 형식

public record Person
{
    public Person([Required] string Name, [Range(0, 100)] int Age)
        => (this.Name, this.Age) = (Name, Age);

    public string Name { get; set; }
    public int Age { get; set; }
}

레코드 형식, 유효성 검사 및 바인딩 메타데이터

레코드 형식의 경우 매개 변수에 대한 유효성 검사 및 바인딩 메타데이터가 사용됩니다. 속성의 메타데이터는 무시됩니다.

public record Person (string Name, int Age)
{
   [BindProperty(Name = "SomeName")] // This does not get used
   [Required] // This does not get used
   public string Name { get; init; }
}

유효성 검사 및 메타데이터

유효성 검사는 매개 변수에 대한 메타데이터를 사용하지만 속성을 사용하여 값을 읽습니다. 기본 생성자를 사용하는 일반적인 경우 두 생성자는 동일합니다. 그러나 이를 무력화하는 방법은 다음과 같습니다.

public record Person([Required] string Name)
{
    private readonly string _name;

    // The following property is never null.
    // However this object could have been constructed as "new Person(null)".
    public string Name { get; init => _name = value ?? string.Empty; }
}

TryUpdateModel은 레코드 형식의 매개 변수를 업데이트하지 않습니다.

public record Person(string Name)
{
    public int Age { get; set; }
}

var person = new Person("initial-name");
TryUpdateModel(person, ...);

이 경우 MVC는 다시 Name을 바인딩하지 않습니다. 그러나 Age를 업데이트할 수 있습니다.

모델 바인딩 경로 데이터 및 쿼리 문자열의 세계화 동작

ASP.NET Core 경로 값 공급자와 쿼리 문자열 값 공급자:

  • 값을 고정 문화권으로 처리합니다.
  • URL을 고정 문화권으로 간주합니다.

이와 대조적으로, 양식 데이터에서 가져온 값은 문화권을 구분하여 변환을 수행합니다. 이는 기본적으로 URL을 로캘 간에 공유할 수 있도록 설계되었습니다.

ASP.NET Core 경로 값 공급자와 쿼리 문자열 값 공급자가 문화권 구분 변환을 수행하도록 하려면 다음을 수행합니다.

public class CultureQueryStringValueProviderFactory : IValueProviderFactory
{
    public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
    {
        _ = context ?? throw new ArgumentNullException(nameof(context));

        var query = context.ActionContext.HttpContext.Request.Query;
        if (query?.Count > 0)
        {
            context.ValueProviders.Add(
                new QueryStringValueProvider(
                    BindingSource.Query,
                    query,
                    CultureInfo.CurrentCulture));
        }

        return Task.CompletedTask;
    }
}
builder.Services.AddControllers(options =>
{
    var index = options.ValueProviderFactories.IndexOf(
        options.ValueProviderFactories.OfType<QueryStringValueProviderFactory>()
            .Single());

    options.ValueProviderFactories[index] =
        new CultureQueryStringValueProviderFactory();
});

특수 데이터 형식

모델 바인딩이 처리할 수 있는 일부 특수 데이터 형식이 있습니다.

IFormFile 및 IFormFileCollection

HTTP 요청에 포함되는 업로드된 파일입니다. 또한 여러 파일에 대해 IEnumerable<IFormFile>이 지원됩니다.

CancellationToken

작업은 필요에 따라 CancellationToken을 매개 변수로 바인딩할 수 있습니다. 그러면 HTTP 요청을 기반으로 하는 연결이 중단될 때 신호를 보내는 RequestAborted가 바인딩됩니다. 작업은 이 매개 변수를 사용하여 컨트롤러 작업의 일부로 실행되는 장기 실행 비동기 작업을 취소할 수 있습니다.

FormCollection

게시된 양식 데이터에서 모든 값을 검색하는 데 사용됩니다.

입력 포맷터

요청 본문의 데이터는 JSON, XML 또는 일부 다른 형식일 수 있습니다. 이 데이터를 구문 분석하기 위해 모델 바인딩은 특정 콘텐츠 유형을 처리하도록 구성된 입력 포맷터를 사용합니다. 기본적으로 ASP.NET Core는 JSON 데이터를 처리하기 위한 JSON 기반 입력 포맷터를 포함합니다. 다른 콘텐츠 형식에 대해 다른 포맷터를 추가할 수 있습니다.

ASP.NET Core는 Consumes 특성을 기반으로 입력 포맷터를 선택합니다. 특성이 없는 경우 Content-Type 헤더를 사용합니다.

기본 제공 XML 입력 포맷터를 사용하려면 다음을 수행합니다.

  • Program.cs에서 AddXmlSerializerFormatters 또는 AddXmlDataContractSerializerFormatters를 호출합니다.

    builder.Services.AddControllers()
        .AddXmlSerializerFormatters();
    
  • 요청 본문에서 XML을 필요로 하는 컨트롤러 클래스 또는 작업 메서드에 Consumes 특성을 적용합니다.

    [HttpPost]
    [Consumes("application/xml")]
    public ActionResult<Pet> Create(Pet pet)
    

    자세한 내용은 XML Serialization 소개를 참조하세요.

입력 포맷터를 사용하여 모델 바인딩 사용자 지정

입력 포맷터는 요청 본문에서 데이터를 읽기 위한 모든 작업을 수행합니다. 이 프로세스를 사용자 지정하려면 입력 포맷터에서 사용하는 API를 구성합니다. 이 섹션에서는 ObjectId라는 사용자 지정 형식을 이해하기 위해 System.Text.Json 기반 입력 포맷터를 사용자 지정하는 방법을 설명합니다.

사용자 지정 ObjectId 속성을 포함하는 다음 모델을 고려합니다.

public class InstructorObjectId
{
    [Required]
    public ObjectId ObjectId { get; set; } = null!;
}

System.Text.Json를 사용할 때 모델 바인딩 프로세스를 사용자 지정하려면 JsonConverter<T>에서 파생된 클래스를 만듭니다.

internal class ObjectIdConverter : JsonConverter<ObjectId>
{
    public override ObjectId Read(
        ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => new(JsonSerializer.Deserialize<int>(ref reader, options));

    public override void Write(
        Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options)
        => writer.WriteNumberValue(value.Id);
}

사용자 지정 변환기를 사용하려면 형식에 JsonConverterAttribute 특성을 적용합니다. 다음 예제에서 ObjectId 형식은 ObjectIdConverter를 사용자 지정 변환기로 사용하여 구성됩니다.

[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);

자세한 내용은 사용자 지정 변환기를 작성하는 방법을 참조하세요.

모델 바인딩에서 지정된 형식 제외

모델 바인딩 및 유효성 검사 시스템의 동작은 .에 의해 ModelMetadata구동됩니다. MvcOptions.ModelMetadataDetailsProviders에 세부 정보 공급 기업을 추가하여 ModelMetadata를 사용자 지정할 수 있습니다. 기본 제공 세부 정보 공급 기업을 지정된 형식에 대한 모델 바인딩 또는 유효성 검사를 비활성화하기 위해 사용할 수 있습니다.

지정된 형식의 모든 모델에 대한 모델 바인딩을 비활성화하려면 Program.cs에서 ExcludeBindingMetadataProvider를 추가합니다. 예를 들어 System.Version 형식의 모든 모델에 대한 모델 바인딩을 비활성화하려면:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.ModelMetadataDetailsProviders.Add(
            new ExcludeBindingMetadataProvider(typeof(Version)));
        options.ModelMetadataDetailsProviders.Add(
            new SuppressChildValidationMetadataProvider(typeof(Guid)));
    });

지정된 형식의 속성에 대한 유효성 검사를 비활성화하려면 Program.cs에서 SuppressChildValidationMetadataProvider를 추가합니다. 예를 들어 System.Guid 형식의 속성에 대한 유효성 검사를 비활성화하려면:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.ModelMetadataDetailsProviders.Add(
            new ExcludeBindingMetadataProvider(typeof(Version)));
        options.ModelMetadataDetailsProviders.Add(
            new SuppressChildValidationMetadataProvider(typeof(Guid)));
    });

사용자 지정 모델 바인더

사용자 지정 모델 바인더를 작성하고 지정된 대상에 대해 선택하도록 [ModelBinder] 특성을 사용하여 모델 바인딩을 확장할 수 있습니다. 사용자 모델 바인딩에 대해 자세히 알아보세요.

수동 모델 바인딩

TryUpdateModelAsync 메서드를 사용하여 모델 바인딩을 수동으로 호출할 수 있습니다. 메서드는 ControllerBasePageModel 클래스에서 정의됩니다. 메서드 오버로드를 통해 사용할 접두사 및 값 공급 기업을 지정할 수 있습니다. 모델 바인딩이 실패하는 경우 메서드는 false를 반환합니다. 예를 들어 다음과 같습니다.

if (await TryUpdateModelAsync(
    newInstructor,
    "Instructor",
    x => x.Name, x => x.HireDate!))
{
    _instructorStore.Add(newInstructor);
    return RedirectToPage("./Index");
}

return Page();

TryUpdateModelAsync는 값 공급자를 사용하여 양식 본문, 쿼리 문자열 및 경로 데이터에서 데이터를 가져옵니다. TryUpdateModelAsync는 일반적으로

  • 과도한 게시를 방지하기 위해 컨트롤러 및 보기를 사용하여 Razor Pages 및 MVC 앱과 함께 사용됩니다.
  • 양식 데이터, 쿼리 문자열 및 경로 데이터에서 사용하지 않는 한 웹 API와 함께 사용되지 않습니다. JSON을 사용하는 웹 API 엔드포인트는 입력 포맷터를 사용하여 요청 본문을 개체로 역직렬화합니다.

자세한 내용은 TryUpdateModelAsync를 참조하세요.

[FromServices] 특성

이 특성의 이름은 데이터 원본을 지정하는 모델 바인딩 특성의 패턴을 따릅니다. 그러나 값 공급 기업의 바인딩 데이터에 대한 것은 아닙니다. 종속성 주입 컨테이너에서 형식의 인스턴스를 가져옵니다. 특정 메서드가 호출되는 경우에만 서비스가 필요할 때 생성자 주입에 대안을 제공하는 것이 목적입니다.

형식의 인스턴스가 종속성 주입 컨테이너에 등록되지 않았을 때 매개 변수를 바인딩하려고 시도하는 경우 앱이 예외를 throw합니다. 매개 변수를 선택 사항으로 만들려면 다음 방법 중 하나를 사용합니다.

  • 매개 변수를 null 허용으로 설정합니다.
  • 매개 변수의 기본값을 설정합니다.

null 허용 매개 변수의 경우 매개 변수에 액세스하기 전에 매개 변수가 null이 아닌지 확인합니다.

추가 리소스

이 문서는 모델 바인딩이 무엇인지, 작동 방법 및 해당 동작을 사용자 지정하는 방법을 설명합니다.

모델 바인딩이란

컨트롤러 및 Razor Pages는 HTTP 요청에서 제공되는 데이터를 사용하여 작동합니다. 예를 들어 경로 데이터는 레코드 키를 제공할 수 있으며, 게시된 양식 필드는 모델의 속성에 대한 값을 제공할 수 있습니다. 이러한 각 값을 검색하고 문자열에서 .NET 형식으로 변환하도록 코드를 작성하는 것은 번거롭고 오류가 발생하기 쉽습니다. 모델 바인딩은 이 프로세스를 자동화합니다. 모델 바인딩 시스템:

  • 경로 데이터, 양식 필드 및 쿼리 문자열과 같은 다양한 원본의 데이터를 검색합니다.
  • 메서드 매개 변수 및 공용 속성에서 컨트롤러 및 Razor Pages에 데이터를 제공합니다.
  • 문자열 데이터를 .NET 형식으로 변환합니다.
  • 복합 형식의 속성을 업데이트합니다.

예시

다음 작업 메서드를 사용합니다.

[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)

앱은 이 URL로 요청을 수신합니다.

https://contoso.com/api/pets/2?DogsOnly=true

모델 바인딩은 라우팅 시스템이 작업 메서드를 선택한 후 다음 단계를 통해 진행됩니다.

  • GetById의 첫 번째 매개 변수, id라는 정수를 찾습니다.
  • HTTP 요청에서 사용 가능한 원본을 찾고 경로 데이터에서 id = "2"를 찾습니다.
  • 문자열 "2"를 정수 2로 변환합니다.
  • GetById의 다음 매개 변수, dogsOnly라는 부울을 찾습니다.
  • 원본을 찾고 쿼리 문자열에서 "DogsOnly=true"를 찾습니다. 이름 일치는 대/소문자를 구분하지 않습니다.
  • 문자열 "true"를 부울 true로 변환합니다.

그런 다음, 프레임워크는 id 매개 변수에 대해 2를 dogsOnly 매개 변수에 대해 true를 전달하는 GetById 메서드를 호출합니다.

이전 예제에서 모델 바인딩 대상은 간단한 형식인 메서드 매개 변수입니다. 대상은 복합 형식의 속성일 수도 있습니다. 각 속성이 성공적으로 바인딩된 후 모델 유효성 검사가 해당 속성에 대해 발생합니다. 모델에 바인딩되는 데이터의 레코드 및 모든 바인딩 또는 유효성 검사 오류는 ControllerBase.ModelState 또는 PageModel.ModelState에 저장됩니다. 이 프로세스가 성공되었는지 확인하기 위해 앱은 ModelState.IsValid 플래그를 확인합니다.

대상

모델 바인딩은 다음 종류의 대상에 대한 값을 찾으려고 합니다.

  • 요청이 라우팅되는 컨트롤러 작업 메서드의 매개 변수
  • 요청이 라우팅되는 Razor Pages 처리기 메서드의 매개 변수
  • 특성으로 지정되는 경우 컨트롤러 또는 PageModel 클래스의 공용 속성

[BindProperty] 특성

모델 바인딩이 해당 속성을 대상으로 하도록 컨트롤러의 공용 속성 또는 PageModel 클래스에 적용할 수 있습니다.

public class EditModel : PageModel
{
    [BindProperty]
    public Instructor? Instructor { get; set; }

    // ...
}

[BindProperties] 특성

모델 바인딩이 클래스의 모든 공용 속성을 대상으로 하도록 컨트롤러 또는 PageModel 클래스에 적용할 수 있습니다.

[BindProperties]
public class CreateModel : PageModel
{
    public Instructor? Instructor { get; set; }

    // ...
}

HTTP GET 요청에 대한 모델 바인딩

기본적으로 속성은 HTTP GET 요청에 대해 바인딩되지 않습니다. 일반적으로 GET 요청에 대해 필요한 것은 레코드 ID 매개 변수입니다. 레코드 ID는 데이터베이스에 있는 항목을 찾는 데 사용됩니다. 따라서 모델의 인스턴스를 포함하는 속성을 바인딩할 필요가 없습니다. 속성을 GET 요청의 데이터에 바인딩하려는 시나리오에서 SupportsGet 속성을 true로 설정합니다.

[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }

단순 및 복합 형식 모델 바인딩

모델 바인딩은 작업을 수행하는 형식에 대한 특정 정의를 사용합니다. 단순 형식은 사용하거나 TryParse 메서드를 사용하여 TypeConverter 단일 문자열에서 변환됩니다. 복합 형식은 여러 입력 값에서 변환됩니다. 프레임워크는 TypeConverter 또는 TryParse 존재 여부에 따라 차이를 결정합니다. 형식 변환기를 만들거나 외부 리소스 또는 여러 입력이 필요하지 않은 SomeType 변환에 string을 위한 TryParse를 사용하는 것이 좋습니다.

원본

기본적으로 모델 바인딩은 HTTP 요청의 다음 원본에서 키-값 쌍의 양식으로 데이터를 가져옵니다.

  1. 양식 필드
  2. 요청 본문([ApiController] 특성이 있는 컨트롤러의 경우)
  3. 경로 데이터
  4. 쿼리 문자열 매개 변수
  5. 업로드된 파일

각 대상 매개 변수 또는 속성의 경우, 소스가 이 목록에 표시된 순서대로 검사됩니다. 몇 가지 예외도 있습니다.

  • 경로 데이터 및 쿼리 문자열 값은 간단한 형식에만 사용됩니다.
  • 업로드된 파일은 IFormFile 또는 IEnumerable<IFormFile>을 구현하는 대상 유형에만 바인딩됩니다.

기본 소스가 올바르지 않으면 다음 특성 중 하나를 사용하여 소스를 지정합니다.

  • [FromQuery] - 쿼리 문자열에서 값을 가져옵니다.
  • [FromRoute] - 경로 데이터에서 값을 가져옵니다.
  • [FromForm] - 게시된 양식 필드에서 값을 가져옵니다.
  • [FromBody] - 요청 본문에서 값을 가져옵니다.
  • [FromHeader] - HTTP 헤더에서 값을 가져옵니다.

이러한 특성:

  • 다음 예제와 같이 모델 클래스가 아닌 모델 속성에 개별적으로 추가됩니다.

    public class Instructor
    {
        public int Id { get; set; }
    
        [FromQuery(Name = "Note")]
        public string? NoteFromQueryString { get; set; }
    
        // ...
    }
    
  • 필요에 따라 생성자에서 모델 이름 값을 허용합니다. 이 옵션은 속성 이름이 요청의 값과 일치하지 않는 경우에 제공됩니다. 예를 들어 요청의 값은 다음 예제와 같이 해당 이름에 하이픈이 있는 헤더일 수 있습니다.

    public void OnGet([FromHeader(Name = "Accept-Language")] string language)
    

[FromBody] 특성

매개 변수에 [FromBody] 특성을 적용하여 HTTP 요청의 본문에서 해당 속성을 채웁니다. ASP.NET Core 런타임은 본문을 읽을 책임을 입력 포맷터에 위임합니다. 입력 포맷터는 이 문서의 뒷부분에 설명되어 있습니다.

[FromBody]이(가) 복합 형식 매개 변수에 적용되는 경우 해당 속성에 적용된 모든 바인딩 소스 특성은 무시됩니다. 예를 들어, 다음 Create 작업은 해당 pet 매개 변수가 본문에서 채워지도록 지정합니다.

public ActionResult<Pet> Create([FromBody] Pet pet)

Pet 클래스는 Breed 속성이 쿼리 문자열 매개 변수에서 채워지도록 지정합니다.

public class Pet
{
    public string Name { get; set; } = null!;

    [FromQuery] // Attribute is ignored.
    public string Breed { get; set; } = null!;
}

앞의 예에서:

  • [FromQuery] 특성은 무시됩니다.
  • Breed 속성은 쿼리 문자열 매개 변수에서 채워지지 않습니다.

입력 포맷터는 본문만 읽고 바인딩 소스 특성은 인식하지 않습니다. 본문에 적절한 값이 있는 경우 해당 값은 Breed 속성을 채우는 데 사용됩니다.

작업 메서드당 둘 이상의 매개 변수에 [FromBody]를 적용하지 마십시오. 입력 포맷터에서 요청 스트림을 읽으면 더 이상 다른 [FromBody] 매개 변수를 바인딩하기 위해 다시 읽을 수 없습니다.

추가 원본

원본 데이터는 값 공급 기업에 의해 모델 바인딩 시스템에 제공됩니다. 다른 원본에서 모델 바인딩에 대한 데이터를 가져오는 사용자 지정 값 공급 기업을 작성 및 등록할 수 있습니다. 예를 들어 쿠키 또는 세션 상태의 데이터를 원할 수 있습니다. 새 원본에서 데이터를 가져오려면 다음을 수행합니다.

  • IValueProvider를 구현하는 클래스를 만듭니다.
  • IValueProviderFactory를 구현하는 클래스를 만듭니다.
  • Program.cs에서 팩터리 클래스를 등록합니다.

이 샘플에는 쿠키에서 값을 가져오는 값 공급자팩터리 예제가 포함되어 있습니다. Program.cs에서 사용자 지정 값 공급자 센터를 등록합니다.

builder.Services.AddControllers(options =>
{
    options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});

이전 코드는 모든 기본 제공 값 공급자 이후에 사용자 지정 값 공급자를 배치합니다. 목록에서 첫 번째로 지정하려면 Add 대신에 Insert(0, new CookieValueProviderFactory())를 호출합니다.

모델 속성에 대한 원본 없음

기본적으로 모델 속성에 대한 값이 없으면 모델 상태 오류가 생성되지 않습니다. 속성은 Null 또는 기본값으로 설정됩니다.

  • Nullable 단순 형식은 null로 설정됩니다.
  • Null을 허용하지 않는 값 형식은 default(T)로 설정됩니다. 예를 들어 매개 변수 int id는 0으로 설정됩니다.
  • 복합 형식의 경우 모델 바인딩은 속성을 설정하지 않고 기본 생성자를 사용하여 인스턴스를 만듭니다.
  • 배열은 byte[] 배열이 null로 설정되는 점을 제외하고 Array.Empty<T>()로 설정됩니다.

모델 속성에 대한 양식 필드에 아무것도 없을 때 모델 상태가 무효화되어야 하는 경우 [BindRequired] 속성을 사용합니다.

[BindRequired] 동작은 요청 본문의 JSON 또는 XML 데이터가 아니라 게시된 양식 데이터의 모델 바인딩에 적용됩니다. 요청 본문 데이터는 입력 포맷터에서 처리됩니다.

형식 변환 오류

원본이 있지만 대상 형식으로 변환될 수 없는 경우 모델 상태는 잘못된 것으로 플래그가 지정됩니다. 이전 섹션에서 설명한 것처럼 대상 매개 변수 또는 속성은 Null 또는 기본값으로 설정됩니다.

[ApiController] 특성이 있는 API 컨트롤러에서 잘못된 모델 상태로 자동 HTTP 400 응답이 발생합니다.

Razor Page에서 페이지를 오류 메시지와 함께 다시 표시합니다.

public IActionResult OnPost()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    // ...

    return RedirectToPage("./Index");
}

이전 코드로 페이지를 다시 표시하면 양식 필드에 유효하지 않은 입력이 표시되지 않습니다. 이는 모델 속성이 Null 또는 기본값으로 설정되었기 때문입니다. 잘못된 입력은 오류 메시지에 표시됩니다. 양식 필드에서 잘못된 데이터를 다시 표시하려면 모델 속성을 문자열로 만들고 데이터 변환을 수동으로 수행하는 것이 좋습니다.

형식 변환 오류를 모델 상태 오류로 만들려 하지 않는 경우 동일한 전략을 권장합니다. 이 경우 모델 속성을 문자열로 만듭니다.

단순 형식

단순 및 복합 형식에 대한 설명은 단순 및 복합 형식 모델 바인딩을 참조하세요.

모델 바인더에서 원본 문자열을 변환할 수 있는 단순 형식은 다음을 포함합니다.

IParsable<T>.TryParse를 사용하여 바인딩

IParsable<TSelf>.TryParse API는 바인딩 컨트롤러 작업 매개 변수 값을 지원합니다.

public static bool TryParse (string? s, IFormatProvider? provider, out TSelf result);

다음 DateRange 클래스는 IParsable<TSelf>을 구현하여 날짜 범위 바인딩을 지원합니다.

public class DateRange : IParsable<DateRange>
{
    public DateOnly? From { get; init; }
    public DateOnly? To { get; init; }

    public static DateRange Parse(string value, IFormatProvider? provider)
    {
        if (!TryParse(value, provider, out var result))
        {
           throw new ArgumentException("Could not parse supplied value.", nameof(value));
        }

        return result;
    }

    public static bool TryParse(string? value,
                                IFormatProvider? provider, out DateRange dateRange)
    {
        var segments = value?.Split(',', StringSplitOptions.RemoveEmptyEntries 
                                       | StringSplitOptions.TrimEntries);

        if (segments?.Length == 2
            && DateOnly.TryParse(segments[0], provider, out var fromDate)
            && DateOnly.TryParse(segments[1], provider, out var toDate))
        {
            dateRange = new DateRange { From = fromDate, To = toDate };
            return true;
        }

        dateRange = new DateRange { From = default, To = default };
        return false;
    }
}

앞의 코드가 하는 역할은 다음과 같습니다.

  • 두 날짜를 나타내는 문자열을 DateRange 개체로 변환합니다.
  • 모델 바인더는 IParsable<TSelf>.TryParse 메서드를 사용하여 DateRange를 바인딩합니다.

다음 컨트롤러 작업은 DateRange 클래스를 사용하여 날짜 범위를 바인딩합니다.

// GET /WeatherForecast/ByRange?range=7/24/2022,07/26/2022
public IActionResult ByRange([FromQuery] DateRange range)
{
    if (!ModelState.IsValid)
        return View("Error", ModelState.Values.SelectMany(v => v.Errors));

    var weatherForecasts = Enumerable
        .Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .Where(wf => DateOnly.FromDateTime(wf.Date) >= range.From
                     && DateOnly.FromDateTime(wf.Date) <= range.To)
        .Select(wf => new WeatherForecastViewModel
        {
            Date = wf.Date.ToString("d"),
            TemperatureC = wf.TemperatureC,
            TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
            Summary = wf.Summary
        });

    return View("Index", weatherForecasts);
}

다음 Locale 클래스는 IParsable<TSelf>을 구현하여 CultureInfo 바인딩을 지원합니다.

public class Locale : CultureInfo, IParsable<Locale>
{
    public Locale(string culture) : base(culture)
    {
    }

    public static Locale Parse(string value, IFormatProvider? provider)
    {
        if (!TryParse(value, provider, out var result))
        {
           throw new ArgumentException("Could not parse supplied value.", nameof(value));
        }

        return result;
    }

    public static bool TryParse([NotNullWhen(true)] string? value,
                                IFormatProvider? provider, out Locale locale)
    {
        if (value is null)
        {
            locale = new Locale(CurrentCulture.Name);
            return false;
        }
        
        try
        {
            locale = new Locale(value);
            return true;
        }
        catch (CultureNotFoundException)
        {
            locale = new Locale(CurrentCulture.Name);
            return false;
        }
    }
}

다음 컨트롤러 작업은 Locale 클래스를 사용하여 CultureInfo 문자열을 바인딩합니다.

// GET /en-GB/WeatherForecast
public IActionResult Index([FromRoute] Locale locale)
{
    var weatherForecasts = Enumerable
        .Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .Select(wf => new WeatherForecastViewModel
        {
            Date = wf.Date.ToString("d", locale),
            TemperatureC = wf.TemperatureC,
            TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
            Summary = wf.Summary
        });

    return View(weatherForecasts);
}

다음 컨트롤러 작업은 DateRangeLocale 클래스를 사용하여 날짜 범위를 CultureInfo로 바인딩합니다.

// GET /af-ZA/WeatherForecast/RangeByLocale?range=2022-07-24,2022-07-29
public IActionResult RangeByLocale([FromRoute] Locale locale, [FromQuery] string range)
{
    if (!ModelState.IsValid)
        return View("Error", ModelState.Values.SelectMany(v => v.Errors));

    if (!DateRange.TryParse(range, locale, out DateRange rangeResult))
    {
        ModelState.TryAddModelError(nameof(range),
            $"Invalid date range: {range} for locale {locale.DisplayName}");

        return View("Error", ModelState.Values.SelectMany(v => v.Errors));
    }

    var weatherForecasts = Enumerable
        .Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .Where(wf => DateOnly.FromDateTime(wf.Date) >= rangeResult.From
                     && DateOnly.FromDateTime(wf.Date) <= rangeResult.To)
        .Select(wf => new WeatherForecastViewModel
        {
            Date = wf.Date.ToString("d", locale),
            TemperatureC = wf.TemperatureC,
            TemperatureF = 32 + (int) (wf.TemperatureC / 0.5556),
            Summary = wf.Summary
        });

    return View("Index", weatherForecasts);
}

GitHub의 API 샘플 앱은 API 컨트롤러에 대한 이전 샘플을 보여줍니다.

TryParse를 사용하여 바인딩

TryParse API는 바인딩 컨트롤러 작업 매개 변수 값을 지원합니다.

public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);

IParsable<T>.TryParseTryParse와 달리 리플렉션에 의존하지 않기 때문에 매개 변수 바인딩에 권장되는 접근 방식입니다.

다음 DateRangeTP 클래스는 TryParse를 구현합니다.

public class DateRangeTP
{
    public DateOnly? From { get; }
    public DateOnly? To { get; }

    public DateRangeTP(string from, string to)
    {
        if (string.IsNullOrEmpty(from))
            throw new ArgumentNullException(nameof(from));
        if (string.IsNullOrEmpty(to))
            throw new ArgumentNullException(nameof(to));

        From = DateOnly.Parse(from);
        To = DateOnly.Parse(to);
    }

    public static bool TryParse(string? value, out DateRangeTP? result)
    {
        var range = value?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
        if (range?.Length != 2)
        {
            result = default;
            return false;
        }

        result = new DateRangeTP(range[0], range[1]);
        return true;
    }
}

다음 컨트롤러 작업은 DateRangeTP 클래스를 사용하여 날짜 범위를 바인딩합니다.

// GET /WeatherForecast/ByRangeTP?range=7/24/2022,07/26/2022
public IActionResult ByRangeTP([FromQuery] DateRangeTP range)
{
    if (!ModelState.IsValid)
        return View("Error", ModelState.Values.SelectMany(v => v.Errors));

    var weatherForecasts = Enumerable
        .Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .Where(wf => DateOnly.FromDateTime(wf.Date) >= range.From
                     && DateOnly.FromDateTime(wf.Date) <= range.To)
        .Select(wf => new WeatherForecastViewModel
        {
            Date = wf.Date.ToString("d"),
            TemperatureC = wf.TemperatureC,
            TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
            Summary = wf.Summary
        });

    return View("Index", weatherForecasts);
}

복합 형식

복합 형식에 공용 기본 생성자와 바인딩할 공용 쓰기 가능 속성이 있어야 합니다. 모델 바인딩이 발생하면 클래스는 공용 기본 생성자를 사용하여 인스턴스화됩니다.

복합 형식 의 각 속성에 대해 모델 바인딩은 이름 패턴 prefix.property_name 원본을 살펴봅니다. 아무것도 없는 경우 접두사 없이 property_name만을 찾습니다. 접두사를 사용할지 여부는 속성별로 결정되는 것이 아닙니다. 예를 들어 메서드 OnGet(Instructor instructor) 바인딩된 ?Instructor.Id=100&Name=foo를 포함하는 쿼리의 경우 Instructor 형식의 결과 개체에는 다음이 포함됩니다.

  • Id(이)가 100(으)로 설정됩니다.
  • Name(이)가 null(으)로 설정됩니다. 이전 쿼리 매개 변수에서 Instructor.Id가 사용되었기 때문에 모델 바인딩에는 Instructor.Name이 필요합니다.

매개 변수에 대한 바인딩의 경우 접두사는 매개 변수 이름입니다. PageModel 공용 속성에 대한 바인딩의 경우 접두사는 공용 속성 이름입니다. 일부 특성에는 매개 변수의 기본 사용 또는 속성 이름을 재정의할 수 있도록 하는 Prefix 속성이 있습니다.

예를 들어 복합 형식이 다음 Instructor 클래스라고 가정합니다.

public class Instructor
{
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
}

접두사 = 매개 변수 이름

바인딩될 모델이 instructorToUpdate라는 매개 변수인 경우:

public IActionResult OnPost(int? id, Instructor instructorToUpdate)

모델 바인딩은 키 instructorToUpdate.ID에 대한 원본을 찾아 시작합니다. 없는 경우 접두사 없이 ID를 찾습니다.

접두사 = 속성 이름

바인딩될 모델이 컨트롤러 또는 PageModel 클래스의 Instructor라는 속성인 경우:

[BindProperty]
public Instructor Instructor { get; set; }

모델 바인딩은 키 Instructor.ID에 대한 원본을 찾아 시작합니다. 없는 경우 접두사 없이 ID를 찾습니다.

사용자 지정 접두사

바인딩될 모델이 instructorToUpdate라는 매개 변수이고 Bind 특성이 접두사로 Instructor를 지정하는 경우:

public IActionResult OnPost(
    int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)

모델 바인딩은 키 Instructor.ID에 대한 원본을 찾아 시작합니다. 없는 경우 접두사 없이 ID를 찾습니다.

복합 형식 대상에 대한 특성

여러 기본 제공 특성은 복합 형식의 모델 바인딩을 제어할 수 있습니다.

Warning

이러한 특성은 게시된 양식 데이터가 값의 원본일 때 모델 바인딩에 영향을 줍니다. 게시된 JSON 및 XML 요청 본문을 처리하는 입력 포맷터에는 영향을 주지 않습니다. 입력 포맷터는 이 문서의 뒷부분에 설명되어 있습니다.

[Bind] 특성

클래스 또는 메서드 매개 변수에 적용될 수 있습니다. 모델 바인딩에 포함되어야 하는 모델의 속성을 지정합니다. [Bind]는 입력 포맷터에 영향을 주지 않습니다.

다음 예제에서 모든 처리기 또는 작업 메서드가 호출될 때 지정된 속성의 Instructor 모델만 바인딩됩니다.

[Bind("LastName,FirstMidName,HireDate")]
public class Instructor

다음 예제에서 OnPost 메서드가 호출될 때 지정된 속성의 Instructor 모델만 바인딩됩니다.

[HttpPost]
public IActionResult OnPost(
    [Bind("LastName,FirstMidName,HireDate")] Instructor instructor)

[Bind] 특성은 만들기 시나리오에서 초과 게시를 방지하는 데 사용될 수 있습니다. 제외된 속성은 변경되지 않은 채로 남겨지는 대신 Null 또는 기본값으로 설정되기 때문에 편집 시나리오에서 잘 작동하지 않습니다. 오버포스트 방지를 위해 특성 대신 [Bind] 보기 모델을 사용하는 것이 좋습니다. 자세한 내용은 초과 게시에 대한 보안 정보를 참조하세요.

[ModelBinder] 특성

ModelBinderAttribute는 형식, 속성 또는 매개 변수에 적용할 수 있습니다. 특정 인스턴스 또는 형식을 바인딩하는 데 사용되는 모델 바인더의 형식을 지정할 수 있습니다. 예시:

[HttpPost]
public IActionResult OnPost(
    [ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)

[ModelBinder] 특성은 모델 바인딩될 때 속성 또는 매개 변수의 이름을 변경하는 데 사용할 수도 있습니다.

public class Instructor
{
    [ModelBinder(Name = "instructor_id")]
    public string Id { get; set; }

    // ...
}

[BindRequired] 특성

모델의 속성에 대한 바인딩이 발생할 수 없는 경우 모델 바인딩이 모델 상태 오류를 추가하도록 합니다. 예를 들어 다음과 같습니다.

public class InstructorBindRequired
{
    // ...

    [BindRequired]
    public DateTime HireDate { get; set; }
}

모델 유효성 검사에서 [Required] 특성의 설명을 참조하세요.

[BindNever] 특성

속성 또는 형식에 적용할 수 있습니다. 모델 바인딩이 모델의 속성을 설정하는 것을 방지합니다. 형식에 적용한 경우 모델 바인딩 시스템은 형식이 정의하는 모든 속성을 제외합니다. 예를 들어 다음과 같습니다.

public class InstructorBindNever
{
    [BindNever]
    public int Id { get; set; }

    // ...
}

컬렉션

단순 형식의 컬렉션인 대상의 경우 모델 바인딩은 parameter_name 또는 property_name에 대한 일치 항목을 찾습니다. 일치하는 항목이 없는 경우 접두사 없이 지원되는 양식 중 하나를 찾습니다. 예시:

  • 바인딩되는 매개 변수가 selectedCourses라는 배열이라고 가정합니다.

    public IActionResult OnPost(int? id, int[] selectedCourses)
    
  • 양식 또는 쿼리 문자열 데이터는 다음 형식 중 하나일 수 있습니다.

    selectedCourses=1050&selectedCourses=2000 
    
    selectedCourses[0]=1050&selectedCourses[1]=2000
    
    [0]=1050&[1]=2000
    
    selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
    
    [a]=1050&[b]=2000&index=a&index=b
    

    index 또는 Index라는 이름의 매개 변수 또는 속성이 컬렉션 값에 인접한 경우에는 바인딩하지 않습니다. 모델 바인딩은 index를 컬렉션의 인덱스로 사용하려고 시도하며 이로 인해 바인딩이 잘못될 수 있습니다. 다음 작업을 예로 들 수 있습니다.

    public IActionResult Post(string index, List<Product> products)
    

    이전 코드에서 index 쿼리 문자열 매개 변수는 index 메서드 매개 변수에 바인딩하며 제품 컬렉션을 바인딩하는 데도 사용합니다. index 매개 변수 이름을 변경하거나 모델 바인딩 특성을 사용하여 바인딩을 구성하면 다음 문제가 발생하지 않습니다.

    public IActionResult Post(string productIndex, List<Product> products)
    
  • 다음 형식은 양식 데이터에서만 지원됩니다.

    selectedCourses[]=1050&selectedCourses[]=2000
    
  • 앞의 모든 예제 형식의 경우 모델 바인딩은 두 항목의 배열을 selectedCourses 매개 변수에 전달합니다.

    • selectedCourses[0]=1050
    • selectedCourses[1]=2000

    아래 첨자 숫자(... [0] ... [1] ...)를 사용하는 데이터 형식은 0부터 시작하여 순차적으로 번호가 매겨지는지 확인해야 합니다. 아래 첨자 번호에 간격이 있는 경우 간격 뒤에 있는 모든 항목은 무시됩니다. 예를 들어 아래 첨자가 0과 1 대신 0과 2인 경우 두 번째 항목은 무시됩니다.

사전

Dictionary 대상의 경우 모델 바인딩은 parameter_name 또는 property_name에 대한 일치 항목을 찾습니다. 일치하는 항목이 없는 경우 접두사 없이 지원되는 양식 중 하나를 찾습니다. 예시:

  • 대상 매개 변수가 selectedCourses라는 Dictionary<int, string>라고 가정합니다.

    public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
    
  • 게시된 양식 또는 쿼리 문자열 데이터는 다음 예제 중 하나와 같을 수 있습니다.

    selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
    
    [1050]=Chemistry&selectedCourses[2000]=Economics
    
    selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry&
    selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
    
    [0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
    
  • 앞의 모든 예제 형식의 경우 모델 바인딩은 두 항목의 사전을 selectedCourses 매개 변수에 전달합니다.

    • selectedCourses["1050"]="Chemistry"
    • selectedCourses["2000"]="Economics"

생성자 바인딩 및 레코드 형식

모델 바인딩을 사용하려면 복합 형식에 매개 변수가 없는 생성자가 있어야 합니다. System.Text.JsonNewtonsoft.Json 기반 입력 포맷터는 모두 매개 변수가 없는 생성자가 없는 클래스의 역직렬화를 지원합니다.

레코드 형식은 네트워크를 통해 데이터를 간결하게 나타내는 좋은 방법입니다. ASP.NET Core는 단일 생성자를 사용하여 모델 바인딩과 레코드 형식의 유효성 검사를 지원합니다.

public record Person(
    [Required] string Name, [Range(0, 150)] int Age, [BindNever] int Id);

public class PersonController
{
    public IActionResult Index() => View();

    [HttpPost]
    public IActionResult Index(Person person)
    {
        // ...
    }
}

Person/Index.cshtml:

@model Person

<label>Name: <input asp-for="Name" /></label>
<br />
<label>Age: <input asp-for="Age" /></label>

레코드 형식의 유효성을 검사할 때 런타임에서는 속성이 아닌 매개 변수에 대한 바인딩 및 유효성 검사 메타데이터를 검색합니다.

프레임워크는 레코드 형식에 대한 바인딩 및 유효성 검사를 허용합니다.

public record Person([Required] string Name, [Range(0, 100)] int Age);

위의 작업을 수행하려면 형식은 다음과 같아야 합니다.

  • 레코드 형식이어야 합니다.
  • 공용 생성자가 하나만 있어야 합니다.
  • 이름 및 형식이 같은 속성을 포함하는 매개 변수를 포함합니다. 경우에 따라 이름이 달라져서는 안 됩니다.

매개 변수가 없는 생성자가 없는 POCO

매개 변수가 없는 생성자가 없는 POCO는 바인딩할 수 없습니다.

다음 코드에서는 형식에 매개 변수가 없는 생성자가 있어야 한다는 예외가 발생합니다.

public class Person(string Name)

public record Person([Required] string Name, [Range(0, 100)] int Age)
{
    public Person(string Name) : this (Name, 0);
}

수동으로 작성된 생성자를 사용하는 레코드 형식

기본 생성자가 작동하는 것처럼 보이는 수동으로 작성된 생성자를 사용하는 레코드 형식

public record Person
{
    public Person([Required] string Name, [Range(0, 100)] int Age)
        => (this.Name, this.Age) = (Name, Age);

    public string Name { get; set; }
    public int Age { get; set; }
}

레코드 형식, 유효성 검사 및 바인딩 메타데이터

레코드 형식의 경우 매개 변수에 대한 유효성 검사 및 바인딩 메타데이터가 사용됩니다. 속성의 메타데이터는 무시됩니다.

public record Person (string Name, int Age)
{
   [BindProperty(Name = "SomeName")] // This does not get used
   [Required] // This does not get used
   public string Name { get; init; }
}

유효성 검사 및 메타데이터

유효성 검사는 매개 변수에 대한 메타데이터를 사용하지만 속성을 사용하여 값을 읽습니다. 기본 생성자를 사용하는 일반적인 경우 두 생성자는 동일합니다. 그러나 이를 무력화하는 방법은 다음과 같습니다.

public record Person([Required] string Name)
{
    private readonly string _name;

    // The following property is never null.
    // However this object could have been constructed as "new Person(null)".
    public string Name { get; init => _name = value ?? string.Empty; }
}

TryUpdateModel은 레코드 형식의 매개 변수를 업데이트하지 않습니다.

public record Person(string Name)
{
    public int Age { get; set; }
}

var person = new Person("initial-name");
TryUpdateModel(person, ...);

이 경우 MVC는 다시 Name을 바인딩하지 않습니다. 그러나 Age를 업데이트할 수 있습니다.

모델 바인딩 경로 데이터 및 쿼리 문자열의 세계화 동작

ASP.NET Core 경로 값 공급자와 쿼리 문자열 값 공급자:

  • 값을 고정 문화권으로 처리합니다.
  • URL을 고정 문화권으로 간주합니다.

이와 대조적으로, 양식 데이터에서 가져온 값은 문화권을 구분하여 변환을 수행합니다. 이는 기본적으로 URL을 로캘 간에 공유할 수 있도록 설계되었습니다.

ASP.NET Core 경로 값 공급자와 쿼리 문자열 값 공급자가 문화권 구분 변환을 수행하도록 하려면 다음을 수행합니다.

public class CultureQueryStringValueProviderFactory : IValueProviderFactory
{
    public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
    {
        _ = context ?? throw new ArgumentNullException(nameof(context));

        var query = context.ActionContext.HttpContext.Request.Query;
        if (query?.Count > 0)
        {
            context.ValueProviders.Add(
                new QueryStringValueProvider(
                    BindingSource.Query,
                    query,
                    CultureInfo.CurrentCulture));
        }

        return Task.CompletedTask;
    }
}
builder.Services.AddControllers(options =>
{
    var index = options.ValueProviderFactories.IndexOf(
        options.ValueProviderFactories.OfType<QueryStringValueProviderFactory>()
            .Single());

    options.ValueProviderFactories[index] =
        new CultureQueryStringValueProviderFactory();
});

특수 데이터 형식

모델 바인딩이 처리할 수 있는 일부 특수 데이터 형식이 있습니다.

IFormFile 및 IFormFileCollection

HTTP 요청에 포함되는 업로드된 파일입니다. 또한 여러 파일에 대해 IEnumerable<IFormFile>이 지원됩니다.

CancellationToken

작업은 필요에 따라 CancellationToken을 매개 변수로 바인딩할 수 있습니다. 그러면 HTTP 요청을 기반으로 하는 연결이 중단될 때 신호를 보내는 RequestAborted가 바인딩됩니다. 작업은 이 매개 변수를 사용하여 컨트롤러 작업의 일부로 실행되는 장기 실행 비동기 작업을 취소할 수 있습니다.

FormCollection

게시된 양식 데이터에서 모든 값을 검색하는 데 사용됩니다.

입력 포맷터

요청 본문의 데이터는 JSON, XML 또는 일부 다른 형식일 수 있습니다. 이 데이터를 구문 분석하기 위해 모델 바인딩은 특정 콘텐츠 유형을 처리하도록 구성된 입력 포맷터를 사용합니다. 기본적으로 ASP.NET Core는 JSON 데이터를 처리하기 위한 JSON 기반 입력 포맷터를 포함합니다. 다른 콘텐츠 형식에 대해 다른 포맷터를 추가할 수 있습니다.

ASP.NET Core는 Consumes 특성을 기반으로 입력 포맷터를 선택합니다. 특성이 없는 경우 Content-Type 헤더를 사용합니다.

기본 제공 XML 입력 포맷터를 사용하려면 다음을 수행합니다.

  • Program.cs에서 AddXmlSerializerFormatters 또는 AddXmlDataContractSerializerFormatters를 호출합니다.

    builder.Services.AddControllers()
        .AddXmlSerializerFormatters();
    
  • 요청 본문에서 XML을 필요로 하는 컨트롤러 클래스 또는 작업 메서드에 Consumes 특성을 적용합니다.

    [HttpPost]
    [Consumes("application/xml")]
    public ActionResult<Pet> Create(Pet pet)
    

    자세한 내용은 XML Serialization 소개를 참조하세요.

입력 포맷터를 사용하여 모델 바인딩 사용자 지정

입력 포맷터는 요청 본문에서 데이터를 읽기 위한 모든 작업을 수행합니다. 이 프로세스를 사용자 지정하려면 입력 포맷터에서 사용하는 API를 구성합니다. 이 섹션에서는 ObjectId라는 사용자 지정 형식을 이해하기 위해 System.Text.Json 기반 입력 포맷터를 사용자 지정하는 방법을 설명합니다.

사용자 지정 ObjectId 속성을 포함하는 다음 모델을 고려합니다.

public class InstructorObjectId
{
    [Required]
    public ObjectId ObjectId { get; set; } = null!;
}

System.Text.Json를 사용할 때 모델 바인딩 프로세스를 사용자 지정하려면 JsonConverter<T>에서 파생된 클래스를 만듭니다.

internal class ObjectIdConverter : JsonConverter<ObjectId>
{
    public override ObjectId Read(
        ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => new(JsonSerializer.Deserialize<int>(ref reader, options));

    public override void Write(
        Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options)
        => writer.WriteNumberValue(value.Id);
}

사용자 지정 변환기를 사용하려면 형식에 JsonConverterAttribute 특성을 적용합니다. 다음 예제에서 ObjectId 형식은 ObjectIdConverter를 사용자 지정 변환기로 사용하여 구성됩니다.

[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);

자세한 내용은 사용자 지정 변환기를 작성하는 방법을 참조하세요.

모델 바인딩에서 지정된 형식 제외

모델 바인딩 및 유효성 검사 시스템의 동작은 .에 의해 ModelMetadata구동됩니다. MvcOptions.ModelMetadataDetailsProviders에 세부 정보 공급 기업을 추가하여 ModelMetadata를 사용자 지정할 수 있습니다. 기본 제공 세부 정보 공급 기업을 지정된 형식에 대한 모델 바인딩 또는 유효성 검사를 비활성화하기 위해 사용할 수 있습니다.

지정된 형식의 모든 모델에 대한 모델 바인딩을 비활성화하려면 Program.cs에서 ExcludeBindingMetadataProvider를 추가합니다. 예를 들어 System.Version 형식의 모든 모델에 대한 모델 바인딩을 비활성화하려면:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.ModelMetadataDetailsProviders.Add(
            new ExcludeBindingMetadataProvider(typeof(Version)));
        options.ModelMetadataDetailsProviders.Add(
            new SuppressChildValidationMetadataProvider(typeof(Guid)));
    });

지정된 형식의 속성에 대한 유효성 검사를 비활성화하려면 Program.cs에서 SuppressChildValidationMetadataProvider를 추가합니다. 예를 들어 System.Guid 형식의 속성에 대한 유효성 검사를 비활성화하려면:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.ModelMetadataDetailsProviders.Add(
            new ExcludeBindingMetadataProvider(typeof(Version)));
        options.ModelMetadataDetailsProviders.Add(
            new SuppressChildValidationMetadataProvider(typeof(Guid)));
    });

사용자 지정 모델 바인더

사용자 지정 모델 바인더를 작성하고 지정된 대상에 대해 선택하도록 [ModelBinder] 특성을 사용하여 모델 바인딩을 확장할 수 있습니다. 사용자 모델 바인딩에 대해 자세히 알아보세요.

수동 모델 바인딩

TryUpdateModelAsync 메서드를 사용하여 모델 바인딩을 수동으로 호출할 수 있습니다. 메서드는 ControllerBasePageModel 클래스에서 정의됩니다. 메서드 오버로드를 통해 사용할 접두사 및 값 공급 기업을 지정할 수 있습니다. 모델 바인딩이 실패하는 경우 메서드는 false를 반환합니다. 예를 들어 다음과 같습니다.

if (await TryUpdateModelAsync(
    newInstructor,
    "Instructor",
    x => x.Name, x => x.HireDate!))
{
    _instructorStore.Add(newInstructor);
    return RedirectToPage("./Index");
}

return Page();

TryUpdateModelAsync는 값 공급자를 사용하여 양식 본문, 쿼리 문자열 및 경로 데이터에서 데이터를 가져옵니다. TryUpdateModelAsync는 일반적으로

  • 과도한 게시를 방지하기 위해 컨트롤러 및 보기를 사용하여 Razor Pages 및 MVC 앱과 함께 사용됩니다.
  • 양식 데이터, 쿼리 문자열 및 경로 데이터에서 사용하지 않는 한 웹 API와 함께 사용되지 않습니다. JSON을 사용하는 웹 API 엔드포인트는 입력 포맷터를 사용하여 요청 본문을 개체로 역직렬화합니다.

자세한 내용은 TryUpdateModelAsync를 참조하세요.

[FromServices] 특성

이 특성의 이름은 데이터 원본을 지정하는 모델 바인딩 특성의 패턴을 따릅니다. 그러나 값 공급 기업의 바인딩 데이터에 대한 것은 아닙니다. 종속성 주입 컨테이너에서 형식의 인스턴스를 가져옵니다. 특정 메서드가 호출되는 경우에만 서비스가 필요할 때 생성자 주입에 대안을 제공하는 것이 목적입니다.

형식의 인스턴스가 종속성 주입 컨테이너에 등록되지 않았을 때 매개 변수를 바인딩하려고 시도하는 경우 앱이 예외를 throw합니다. 매개 변수를 선택 사항으로 만들려면 다음 방법 중 하나를 사용합니다.

  • 매개 변수를 null 허용으로 설정합니다.
  • 매개 변수의 기본값을 설정합니다.

null 허용 매개 변수의 경우 매개 변수에 액세스하기 전에 매개 변수가 null이 아닌지 확인합니다.

추가 리소스

이 문서는 모델 바인딩이 무엇인지, 작동 방법 및 해당 동작을 사용자 지정하는 방법을 설명합니다.

모델 바인딩이란

컨트롤러 및 Razor Pages는 HTTP 요청에서 제공되는 데이터를 사용하여 작동합니다. 예를 들어 경로 데이터는 레코드 키를 제공할 수 있으며, 게시된 양식 필드는 모델의 속성에 대한 값을 제공할 수 있습니다. 이러한 각 값을 검색하고 문자열에서 .NET 형식으로 변환하도록 코드를 작성하는 것은 번거롭고 오류가 발생하기 쉽습니다. 모델 바인딩은 이 프로세스를 자동화합니다. 모델 바인딩 시스템:

  • 경로 데이터, 양식 필드 및 쿼리 문자열과 같은 다양한 원본의 데이터를 검색합니다.
  • 메서드 매개 변수 및 공용 속성에서 컨트롤러 및 Razor Pages에 데이터를 제공합니다.
  • 문자열 데이터를 .NET 형식으로 변환합니다.
  • 복합 형식의 속성을 업데이트합니다.

예시

다음 작업 메서드를 사용합니다.

[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)

앱은 이 URL로 요청을 수신합니다.

https://contoso.com/api/pets/2?DogsOnly=true

모델 바인딩은 라우팅 시스템이 작업 메서드를 선택한 후 다음 단계를 통해 진행됩니다.

  • GetById의 첫 번째 매개 변수, id라는 정수를 찾습니다.
  • HTTP 요청에서 사용 가능한 원본을 찾고 경로 데이터에서 id = "2"를 찾습니다.
  • 문자열 "2"를 정수 2로 변환합니다.
  • GetById의 다음 매개 변수, dogsOnly라는 부울을 찾습니다.
  • 원본을 찾고 쿼리 문자열에서 "DogsOnly=true"를 찾습니다. 이름 일치는 대/소문자를 구분하지 않습니다.
  • 문자열 "true"를 부울 true로 변환합니다.

그런 다음, 프레임워크는 id 매개 변수에 대해 2를 dogsOnly 매개 변수에 대해 true를 전달하는 GetById 메서드를 호출합니다.

이전 예제에서 모델 바인딩 대상은 간단한 형식인 메서드 매개 변수입니다. 대상은 복합 형식의 속성일 수도 있습니다. 각 속성이 성공적으로 바인딩된 후 모델 유효성 검사가 해당 속성에 대해 발생합니다. 모델에 바인딩되는 데이터의 레코드 및 모든 바인딩 또는 유효성 검사 오류는 ControllerBase.ModelState 또는 PageModel.ModelState에 저장됩니다. 이 프로세스가 성공되었는지 확인하기 위해 앱은 ModelState.IsValid 플래그를 확인합니다.

대상

모델 바인딩은 다음 종류의 대상에 대한 값을 찾으려고 합니다.

  • 요청이 라우팅되는 컨트롤러 작업 메서드의 매개 변수
  • 요청이 라우팅되는 Razor Pages 처리기 메서드의 매개 변수
  • 특성으로 지정되는 경우 컨트롤러 또는 PageModel 클래스의 공용 속성

[BindProperty] 특성

모델 바인딩이 해당 속성을 대상으로 하도록 컨트롤러의 공용 속성 또는 PageModel 클래스에 적용할 수 있습니다.

public class EditModel : PageModel
{
    [BindProperty]
    public Instructor? Instructor { get; set; }

    // ...
}

[BindProperties] 특성

모델 바인딩이 클래스의 모든 공용 속성을 대상으로 하도록 컨트롤러 또는 PageModel 클래스에 적용할 수 있습니다.

[BindProperties]
public class CreateModel : PageModel
{
    public Instructor? Instructor { get; set; }

    // ...
}

HTTP GET 요청에 대한 모델 바인딩

기본적으로 속성은 HTTP GET 요청에 대해 바인딩되지 않습니다. 일반적으로 GET 요청에 대해 필요한 것은 레코드 ID 매개 변수입니다. 레코드 ID는 데이터베이스에 있는 항목을 찾는 데 사용됩니다. 따라서 모델의 인스턴스를 포함하는 속성을 바인딩할 필요가 없습니다. 속성을 GET 요청의 데이터에 바인딩하려는 시나리오에서 SupportsGet 속성을 true로 설정합니다.

[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }

원본

기본적으로 모델 바인딩은 HTTP 요청의 다음 원본에서 키-값 쌍의 양식으로 데이터를 가져옵니다.

  1. 양식 필드
  2. 요청 본문([ApiController] 특성이 있는 컨트롤러의 경우)
  3. 경로 데이터
  4. 쿼리 문자열 매개 변수
  5. 업로드된 파일

각 대상 매개 변수 또는 속성의 경우, 소스가 이 목록에 표시된 순서대로 검사됩니다. 몇 가지 예외도 있습니다.

  • 경로 데이터 및 쿼리 문자열 값은 간단한 형식에만 사용됩니다.
  • 업로드된 파일은 IFormFile 또는 IEnumerable<IFormFile>을 구현하는 대상 유형에만 바인딩됩니다.

기본 소스가 올바르지 않으면 다음 특성 중 하나를 사용하여 소스를 지정합니다.

  • [FromQuery] - 쿼리 문자열에서 값을 가져옵니다.
  • [FromRoute] - 경로 데이터에서 값을 가져옵니다.
  • [FromForm] - 게시된 양식 필드에서 값을 가져옵니다.
  • [FromBody] - 요청 본문에서 값을 가져옵니다.
  • [FromHeader] - HTTP 헤더에서 값을 가져옵니다.

이러한 특성:

  • 다음 예제와 같이 모델 클래스가 아닌 모델 속성에 개별적으로 추가됩니다.

    public class Instructor
    {
        public int Id { get; set; }
    
        [FromQuery(Name = "Note")]
        public string? NoteFromQueryString { get; set; }
    
        // ...
    }
    
  • 필요에 따라 생성자에서 모델 이름 값을 허용합니다. 이 옵션은 속성 이름이 요청의 값과 일치하지 않는 경우에 제공됩니다. 예를 들어 요청의 값은 다음 예제와 같이 해당 이름에 하이픈이 있는 헤더일 수 있습니다.

    public void OnGet([FromHeader(Name = "Accept-Language")] string language)
    

[FromBody] 특성

매개 변수에 [FromBody] 특성을 적용하여 HTTP 요청의 본문에서 해당 속성을 채웁니다. ASP.NET Core 런타임은 본문을 읽을 책임을 입력 포맷터에 위임합니다. 입력 포맷터는 이 문서의 뒷부분에 설명되어 있습니다.

[FromBody]이(가) 복합 형식 매개 변수에 적용되는 경우 해당 속성에 적용된 모든 바인딩 소스 특성은 무시됩니다. 예를 들어, 다음 Create 작업은 해당 pet 매개 변수가 본문에서 채워지도록 지정합니다.

public ActionResult<Pet> Create([FromBody] Pet pet)

Pet 클래스는 Breed 속성이 쿼리 문자열 매개 변수에서 채워지도록 지정합니다.

public class Pet
{
    public string Name { get; set; } = null!;

    [FromQuery] // Attribute is ignored.
    public string Breed { get; set; } = null!;
}

앞의 예에서:

  • [FromQuery] 특성은 무시됩니다.
  • Breed 속성은 쿼리 문자열 매개 변수에서 채워지지 않습니다.

입력 포맷터는 본문만 읽고 바인딩 소스 특성은 인식하지 않습니다. 본문에 적절한 값이 있는 경우 해당 값은 Breed 속성을 채우는 데 사용됩니다.

작업 메서드당 둘 이상의 매개 변수에 [FromBody]를 적용하지 마십시오. 입력 포맷터에서 요청 스트림을 읽으면 더 이상 다른 [FromBody] 매개 변수를 바인딩하기 위해 다시 읽을 수 없습니다.

추가 원본

원본 데이터는 값 공급 기업에 의해 모델 바인딩 시스템에 제공됩니다. 다른 원본에서 모델 바인딩에 대한 데이터를 가져오는 사용자 지정 값 공급 기업을 작성 및 등록할 수 있습니다. 예를 들어 쿠키 또는 세션 상태의 데이터를 원할 수 있습니다. 새 원본에서 데이터를 가져오려면 다음을 수행합니다.

  • IValueProvider를 구현하는 클래스를 만듭니다.
  • IValueProviderFactory를 구현하는 클래스를 만듭니다.
  • Program.cs에서 팩터리 클래스를 등록합니다.

이 샘플에는 쿠키에서 값을 가져오는 값 공급자팩터리 예제가 포함되어 있습니다. Program.cs에서 사용자 지정 값 공급자 센터를 등록합니다.

builder.Services.AddControllers(options =>
{
    options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});

이전 코드는 모든 기본 제공 값 공급자 이후에 사용자 지정 값 공급자를 배치합니다. 목록에서 첫 번째로 지정하려면 Add 대신에 Insert(0, new CookieValueProviderFactory())를 호출합니다.

모델 속성에 대한 원본 없음

기본적으로 모델 속성에 대한 값이 없으면 모델 상태 오류가 생성되지 않습니다. 속성은 Null 또는 기본값으로 설정됩니다.

  • Nullable 단순 형식은 null로 설정됩니다.
  • Null을 허용하지 않는 값 형식은 default(T)로 설정됩니다. 예를 들어 매개 변수 int id는 0으로 설정됩니다.
  • 복합 형식의 경우 모델 바인딩은 속성을 설정하지 않고 기본 생성자를 사용하여 인스턴스를 만듭니다.
  • 배열은 byte[] 배열이 null로 설정되는 점을 제외하고 Array.Empty<T>()로 설정됩니다.

모델 속성에 대한 양식 필드에 아무것도 없을 때 모델 상태가 무효화되어야 하는 경우 [BindRequired] 속성을 사용합니다.

[BindRequired] 동작은 요청 본문의 JSON 또는 XML 데이터가 아니라 게시된 양식 데이터의 모델 바인딩에 적용됩니다. 요청 본문 데이터는 입력 포맷터에서 처리됩니다.

형식 변환 오류

원본이 있지만 대상 형식으로 변환될 수 없는 경우 모델 상태는 잘못된 것으로 플래그가 지정됩니다. 이전 섹션에서 설명한 것처럼 대상 매개 변수 또는 속성은 Null 또는 기본값으로 설정됩니다.

[ApiController] 특성이 있는 API 컨트롤러에서 잘못된 모델 상태로 자동 HTTP 400 응답이 발생합니다.

Razor Page에서 페이지를 오류 메시지와 함께 다시 표시합니다.

public IActionResult OnPost()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    // ...

    return RedirectToPage("./Index");
}

이전 코드로 페이지를 다시 표시하면 양식 필드에 유효하지 않은 입력이 표시되지 않습니다. 이는 모델 속성이 Null 또는 기본값으로 설정되었기 때문입니다. 잘못된 입력은 오류 메시지에 표시됩니다. 양식 필드에서 잘못된 데이터를 다시 표시하려면 모델 속성을 문자열로 만들고 데이터 변환을 수동으로 수행하는 것이 좋습니다.

형식 변환 오류를 모델 상태 오류로 만들려 하지 않는 경우 동일한 전략을 권장합니다. 이 경우 모델 속성을 문자열로 만듭니다.

단순 형식

모델 바인더에서 원본 문자열을 변환할 수 있는 단순 형식은 다음을 포함합니다.

복합 형식

복합 형식에 공용 기본 생성자와 바인딩할 공용 쓰기 가능 속성이 있어야 합니다. 모델 바인딩이 발생하면 클래스는 공용 기본 생성자를 사용하여 인스턴스화됩니다.

복합 형식 의 각 속성에 대해 모델 바인딩은 이름 패턴 prefix.property_name 원본을 살펴봅니다. 아무것도 없는 경우 접두사 없이 property_name만을 찾습니다. 접두사를 사용할지 여부는 속성별로 결정되는 것이 아닙니다. 예를 들어 메서드 OnGet(Instructor instructor) 바인딩된 ?Instructor.Id=100&Name=foo를 포함하는 쿼리의 경우 Instructor 형식의 결과 개체에는 다음이 포함됩니다.

  • Id(이)가 100(으)로 설정됩니다.
  • Name(이)가 null(으)로 설정됩니다. 이전 쿼리 매개 변수에서 Instructor.Id가 사용되었기 때문에 모델 바인딩에는 Instructor.Name이 필요합니다.

매개 변수에 대한 바인딩의 경우 접두사는 매개 변수 이름입니다. PageModel 공용 속성에 대한 바인딩의 경우 접두사는 공용 속성 이름입니다. 일부 특성에는 매개 변수의 기본 사용 또는 속성 이름을 재정의할 수 있도록 하는 Prefix 속성이 있습니다.

예를 들어 복합 형식이 다음 Instructor 클래스라고 가정합니다.

public class Instructor
{
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
}

접두사 = 매개 변수 이름

바인딩될 모델이 instructorToUpdate라는 매개 변수인 경우:

public IActionResult OnPost(int? id, Instructor instructorToUpdate)

모델 바인딩은 키 instructorToUpdate.ID에 대한 원본을 찾아 시작합니다. 없는 경우 접두사 없이 ID를 찾습니다.

접두사 = 속성 이름

바인딩될 모델이 컨트롤러 또는 PageModel 클래스의 Instructor라는 속성인 경우:

[BindProperty]
public Instructor Instructor { get; set; }

모델 바인딩은 키 Instructor.ID에 대한 원본을 찾아 시작합니다. 없는 경우 접두사 없이 ID를 찾습니다.

사용자 지정 접두사

바인딩될 모델이 instructorToUpdate라는 매개 변수이고 Bind 특성이 접두사로 Instructor를 지정하는 경우:

public IActionResult OnPost(
    int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)

모델 바인딩은 키 Instructor.ID에 대한 원본을 찾아 시작합니다. 없는 경우 접두사 없이 ID를 찾습니다.

복합 형식 대상에 대한 특성

여러 기본 제공 특성은 복합 형식의 모델 바인딩을 제어할 수 있습니다.

Warning

이러한 특성은 게시된 양식 데이터가 값의 원본일 때 모델 바인딩에 영향을 줍니다. 게시된 JSON 및 XML 요청 본문을 처리하는 입력 포맷터에는 영향을 주지 않습니다. 입력 포맷터는 이 문서의 뒷부분에 설명되어 있습니다.

[Bind] 특성

클래스 또는 메서드 매개 변수에 적용될 수 있습니다. 모델 바인딩에 포함되어야 하는 모델의 속성을 지정합니다. [Bind]는 입력 포맷터에 영향을 주지 않습니다.

다음 예제에서 모든 처리기 또는 작업 메서드가 호출될 때 지정된 속성의 Instructor 모델만 바인딩됩니다.

[Bind("LastName,FirstMidName,HireDate")]
public class Instructor

다음 예제에서 OnPost 메서드가 호출될 때 지정된 속성의 Instructor 모델만 바인딩됩니다.

[HttpPost]
public IActionResult OnPost(
    [Bind("LastName,FirstMidName,HireDate")] Instructor instructor)

[Bind] 특성은 만들기 시나리오에서 초과 게시를 방지하는 데 사용될 수 있습니다. 제외된 속성은 변경되지 않은 채로 남겨지는 대신 Null 또는 기본값으로 설정되기 때문에 편집 시나리오에서 잘 작동하지 않습니다. 오버포스트 방지를 위해 특성 대신 [Bind] 보기 모델을 사용하는 것이 좋습니다. 자세한 내용은 초과 게시에 대한 보안 정보를 참조하세요.

[ModelBinder] 특성

ModelBinderAttribute는 형식, 속성 또는 매개 변수에 적용할 수 있습니다. 특정 인스턴스 또는 형식을 바인딩하는 데 사용되는 모델 바인더의 형식을 지정할 수 있습니다. 예시:

[HttpPost]
public IActionResult OnPost(
    [ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)

[ModelBinder] 특성은 모델 바인딩될 때 속성 또는 매개 변수의 이름을 변경하는 데 사용할 수도 있습니다.

public class Instructor
{
    [ModelBinder(Name = "instructor_id")]
    public string Id { get; set; }

    // ...
}

[BindRequired] 특성

모델의 속성에 대한 바인딩이 발생할 수 없는 경우 모델 바인딩이 모델 상태 오류를 추가하도록 합니다. 예를 들어 다음과 같습니다.

public class InstructorBindRequired
{
    // ...

    [BindRequired]
    public DateTime HireDate { get; set; }
}

모델 유효성 검사에서 [Required] 특성의 설명을 참조하세요.

[BindNever] 특성

속성 또는 형식에 적용할 수 있습니다. 모델 바인딩이 모델의 속성을 설정하는 것을 방지합니다. 형식에 적용한 경우 모델 바인딩 시스템은 형식이 정의하는 모든 속성을 제외합니다. 예를 들어 다음과 같습니다.

public class InstructorBindNever
{
    [BindNever]
    public int Id { get; set; }

    // ...
}

컬렉션

단순 형식의 컬렉션인 대상의 경우 모델 바인딩은 parameter_name 또는 property_name에 대한 일치 항목을 찾습니다. 일치하는 항목이 없는 경우 접두사 없이 지원되는 양식 중 하나를 찾습니다. 예시:

  • 바인딩되는 매개 변수가 selectedCourses라는 배열이라고 가정합니다.

    public IActionResult OnPost(int? id, int[] selectedCourses)
    
  • 양식 또는 쿼리 문자열 데이터는 다음 형식 중 하나일 수 있습니다.

    selectedCourses=1050&selectedCourses=2000 
    
    selectedCourses[0]=1050&selectedCourses[1]=2000
    
    [0]=1050&[1]=2000
    
    selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
    
    [a]=1050&[b]=2000&index=a&index=b
    

    index 또는 Index라는 이름의 매개 변수 또는 속성이 컬렉션 값에 인접한 경우에는 바인딩하지 않습니다. 모델 바인딩은 index를 컬렉션의 인덱스로 사용하려고 시도하며 이로 인해 바인딩이 잘못될 수 있습니다. 다음 작업을 예로 들 수 있습니다.

    public IActionResult Post(string index, List<Product> products)
    

    이전 코드에서 index 쿼리 문자열 매개 변수는 index 메서드 매개 변수에 바인딩하며 제품 컬렉션을 바인딩하는 데도 사용합니다. index 매개 변수 이름을 변경하거나 모델 바인딩 특성을 사용하여 바인딩을 구성하면 다음 문제가 발생하지 않습니다.

    public IActionResult Post(string productIndex, List<Product> products)
    
  • 다음 형식은 양식 데이터에서만 지원됩니다.

    selectedCourses[]=1050&selectedCourses[]=2000
    
  • 앞의 모든 예제 형식의 경우 모델 바인딩은 두 항목의 배열을 selectedCourses 매개 변수에 전달합니다.

    • selectedCourses[0]=1050
    • selectedCourses[1]=2000

    아래 첨자 숫자(... [0] ... [1] ...)를 사용하는 데이터 형식은 0부터 시작하여 순차적으로 번호가 매겨지는지 확인해야 합니다. 아래 첨자 번호에 간격이 있는 경우 간격 뒤에 있는 모든 항목은 무시됩니다. 예를 들어 아래 첨자가 0과 1 대신 0과 2인 경우 두 번째 항목은 무시됩니다.

사전

Dictionary 대상의 경우 모델 바인딩은 parameter_name 또는 property_name에 대한 일치 항목을 찾습니다. 일치하는 항목이 없는 경우 접두사 없이 지원되는 양식 중 하나를 찾습니다. 예시:

  • 대상 매개 변수가 selectedCourses라는 Dictionary<int, string>라고 가정합니다.

    public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
    
  • 게시된 양식 또는 쿼리 문자열 데이터는 다음 예제 중 하나와 같을 수 있습니다.

    selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
    
    [1050]=Chemistry&selectedCourses[2000]=Economics
    
    selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry&
    selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
    
    [0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
    
  • 앞의 모든 예제 형식의 경우 모델 바인딩은 두 항목의 사전을 selectedCourses 매개 변수에 전달합니다.

    • selectedCourses["1050"]="Chemistry"
    • selectedCourses["2000"]="Economics"

생성자 바인딩 및 레코드 형식

모델 바인딩을 사용하려면 복합 형식에 매개 변수가 없는 생성자가 있어야 합니다. System.Text.JsonNewtonsoft.Json 기반 입력 포맷터는 모두 매개 변수가 없는 생성자가 없는 클래스의 역직렬화를 지원합니다.

레코드 형식은 네트워크를 통해 데이터를 간결하게 나타내는 좋은 방법입니다. ASP.NET Core는 단일 생성자를 사용하여 모델 바인딩과 레코드 형식의 유효성 검사를 지원합니다.

public record Person(
    [Required] string Name, [Range(0, 150)] int Age, [BindNever] int Id);

public class PersonController
{
    public IActionResult Index() => View();

    [HttpPost]
    public IActionResult Index(Person person)
    {
        // ...
    }
}

Person/Index.cshtml:

@model Person

<label>Name: <input asp-for="Name" /></label>
<br />
<label>Age: <input asp-for="Age" /></label>

레코드 형식의 유효성을 검사할 때 런타임에서는 속성이 아닌 매개 변수에 대한 바인딩 및 유효성 검사 메타데이터를 검색합니다.

프레임워크는 레코드 형식에 대한 바인딩 및 유효성 검사를 허용합니다.

public record Person([Required] string Name, [Range(0, 100)] int Age);

위의 작업을 수행하려면 형식은 다음과 같아야 합니다.

  • 레코드 형식이어야 합니다.
  • 공용 생성자가 하나만 있어야 합니다.
  • 이름 및 형식이 같은 속성을 포함하는 매개 변수를 포함합니다. 경우에 따라 이름이 달라져서는 안 됩니다.

매개 변수가 없는 생성자가 없는 POCO

매개 변수가 없는 생성자가 없는 POCO는 바인딩할 수 없습니다.

다음 코드에서는 형식에 매개 변수가 없는 생성자가 있어야 한다는 예외가 발생합니다.

public class Person(string Name)

public record Person([Required] string Name, [Range(0, 100)] int Age)
{
    public Person(string Name) : this (Name, 0);
}

수동으로 작성된 생성자를 사용하는 레코드 형식

기본 생성자가 작동하는 것처럼 보이는 수동으로 작성된 생성자를 사용하는 레코드 형식

public record Person
{
    public Person([Required] string Name, [Range(0, 100)] int Age)
        => (this.Name, this.Age) = (Name, Age);

    public string Name { get; set; }
    public int Age { get; set; }
}

레코드 형식, 유효성 검사 및 바인딩 메타데이터

레코드 형식의 경우 매개 변수에 대한 유효성 검사 및 바인딩 메타데이터가 사용됩니다. 속성의 메타데이터는 무시됩니다.

public record Person (string Name, int Age)
{
   [BindProperty(Name = "SomeName")] // This does not get used
   [Required] // This does not get used
   public string Name { get; init; }
}

유효성 검사 및 메타데이터

유효성 검사는 매개 변수에 대한 메타데이터를 사용하지만 속성을 사용하여 값을 읽습니다. 기본 생성자를 사용하는 일반적인 경우 두 생성자는 동일합니다. 그러나 이를 무력화하는 방법은 다음과 같습니다.

public record Person([Required] string Name)
{
    private readonly string _name;

    // The following property is never null.
    // However this object could have been constructed as "new Person(null)".
    public string Name { get; init => _name = value ?? string.Empty; }
}

TryUpdateModel은 레코드 형식의 매개 변수를 업데이트하지 않습니다.

public record Person(string Name)
{
    public int Age { get; set; }
}

var person = new Person("initial-name");
TryUpdateModel(person, ...);

이 경우 MVC는 다시 Name을 바인딩하지 않습니다. 그러나 Age를 업데이트할 수 있습니다.

모델 바인딩 경로 데이터 및 쿼리 문자열의 세계화 동작

ASP.NET Core 경로 값 공급자와 쿼리 문자열 값 공급자:

  • 값을 고정 문화권으로 처리합니다.
  • URL을 고정 문화권으로 간주합니다.

이와 대조적으로, 양식 데이터에서 가져온 값은 문화권을 구분하여 변환을 수행합니다. 이는 기본적으로 URL을 로캘 간에 공유할 수 있도록 설계되었습니다.

ASP.NET Core 경로 값 공급자와 쿼리 문자열 값 공급자가 문화권 구분 변환을 수행하도록 하려면 다음을 수행합니다.

public class CultureQueryStringValueProviderFactory : IValueProviderFactory
{
    public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
    {
        _ = context ?? throw new ArgumentNullException(nameof(context));

        var query = context.ActionContext.HttpContext.Request.Query;
        if (query?.Count > 0)
        {
            context.ValueProviders.Add(
                new QueryStringValueProvider(
                    BindingSource.Query,
                    query,
                    CultureInfo.CurrentCulture));
        }

        return Task.CompletedTask;
    }
}
builder.Services.AddControllers(options =>
{
    var index = options.ValueProviderFactories.IndexOf(
        options.ValueProviderFactories.OfType<QueryStringValueProviderFactory>()
            .Single());

    options.ValueProviderFactories[index] =
        new CultureQueryStringValueProviderFactory();
});

특수 데이터 형식

모델 바인딩이 처리할 수 있는 일부 특수 데이터 형식이 있습니다.

IFormFile 및 IFormFileCollection

HTTP 요청에 포함되는 업로드된 파일입니다. 또한 여러 파일에 대해 IEnumerable<IFormFile>이 지원됩니다.

CancellationToken

작업은 필요에 따라 CancellationToken을 매개 변수로 바인딩할 수 있습니다. 그러면 HTTP 요청을 기반으로 하는 연결이 중단될 때 신호를 보내는 RequestAborted가 바인딩됩니다. 작업은 이 매개 변수를 사용하여 컨트롤러 작업의 일부로 실행되는 장기 실행 비동기 작업을 취소할 수 있습니다.

FormCollection

게시된 양식 데이터에서 모든 값을 검색하는 데 사용됩니다.

입력 포맷터

요청 본문의 데이터는 JSON, XML 또는 일부 다른 형식일 수 있습니다. 이 데이터를 구문 분석하기 위해 모델 바인딩은 특정 콘텐츠 유형을 처리하도록 구성된 입력 포맷터를 사용합니다. 기본적으로 ASP.NET Core는 JSON 데이터를 처리하기 위한 JSON 기반 입력 포맷터를 포함합니다. 다른 콘텐츠 형식에 대해 다른 포맷터를 추가할 수 있습니다.

ASP.NET Core는 Consumes 특성을 기반으로 입력 포맷터를 선택합니다. 특성이 없는 경우 Content-Type 헤더를 사용합니다.

기본 제공 XML 입력 포맷터를 사용하려면 다음을 수행합니다.

  • Program.cs에서 AddXmlSerializerFormatters 또는 AddXmlDataContractSerializerFormatters를 호출합니다.

    builder.Services.AddControllers()
        .AddXmlSerializerFormatters();
    
  • 요청 본문에서 XML을 필요로 하는 컨트롤러 클래스 또는 작업 메서드에 Consumes 특성을 적용합니다.

    [HttpPost]
    [Consumes("application/xml")]
    public ActionResult<Pet> Create(Pet pet)
    

    자세한 내용은 XML Serialization 소개를 참조하세요.

입력 포맷터를 사용하여 모델 바인딩 사용자 지정

입력 포맷터는 요청 본문에서 데이터를 읽기 위한 모든 작업을 수행합니다. 이 프로세스를 사용자 지정하려면 입력 포맷터에서 사용하는 API를 구성합니다. 이 섹션에서는 ObjectId라는 사용자 지정 형식을 이해하기 위해 System.Text.Json 기반 입력 포맷터를 사용자 지정하는 방법을 설명합니다.

사용자 지정 ObjectId 속성을 포함하는 다음 모델을 고려합니다.

public class InstructorObjectId
{
    [Required]
    public ObjectId ObjectId { get; set; } = null!;
}

System.Text.Json를 사용할 때 모델 바인딩 프로세스를 사용자 지정하려면 JsonConverter<T>에서 파생된 클래스를 만듭니다.

internal class ObjectIdConverter : JsonConverter<ObjectId>
{
    public override ObjectId Read(
        ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => new(JsonSerializer.Deserialize<int>(ref reader, options));

    public override void Write(
        Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options)
        => writer.WriteNumberValue(value.Id);
}

사용자 지정 변환기를 사용하려면 형식에 JsonConverterAttribute 특성을 적용합니다. 다음 예제에서 ObjectId 형식은 ObjectIdConverter를 사용자 지정 변환기로 사용하여 구성됩니다.

[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);

자세한 내용은 사용자 지정 변환기를 작성하는 방법을 참조하세요.

모델 바인딩에서 지정된 형식 제외

모델 바인딩 및 유효성 검사 시스템의 동작은 .에 의해 ModelMetadata구동됩니다. MvcOptions.ModelMetadataDetailsProviders에 세부 정보 공급 기업을 추가하여 ModelMetadata를 사용자 지정할 수 있습니다. 기본 제공 세부 정보 공급 기업을 지정된 형식에 대한 모델 바인딩 또는 유효성 검사를 비활성화하기 위해 사용할 수 있습니다.

지정된 형식의 모든 모델에 대한 모델 바인딩을 비활성화하려면 Program.cs에서 ExcludeBindingMetadataProvider를 추가합니다. 예를 들어 System.Version 형식의 모든 모델에 대한 모델 바인딩을 비활성화하려면:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.ModelMetadataDetailsProviders.Add(
            new ExcludeBindingMetadataProvider(typeof(Version)));
        options.ModelMetadataDetailsProviders.Add(
            new SuppressChildValidationMetadataProvider(typeof(Guid)));
    });

지정된 형식의 속성에 대한 유효성 검사를 비활성화하려면 Program.cs에서 SuppressChildValidationMetadataProvider를 추가합니다. 예를 들어 System.Guid 형식의 속성에 대한 유효성 검사를 비활성화하려면:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.ModelMetadataDetailsProviders.Add(
            new ExcludeBindingMetadataProvider(typeof(Version)));
        options.ModelMetadataDetailsProviders.Add(
            new SuppressChildValidationMetadataProvider(typeof(Guid)));
    });

사용자 지정 모델 바인더

사용자 지정 모델 바인더를 작성하고 지정된 대상에 대해 선택하도록 [ModelBinder] 특성을 사용하여 모델 바인딩을 확장할 수 있습니다. 사용자 모델 바인딩에 대해 자세히 알아보세요.

수동 모델 바인딩

TryUpdateModelAsync 메서드를 사용하여 모델 바인딩을 수동으로 호출할 수 있습니다. 메서드는 ControllerBasePageModel 클래스에서 정의됩니다. 메서드 오버로드를 통해 사용할 접두사 및 값 공급 기업을 지정할 수 있습니다. 모델 바인딩이 실패하는 경우 메서드는 false를 반환합니다. 예를 들어 다음과 같습니다.

if (await TryUpdateModelAsync(
    newInstructor,
    "Instructor",
    x => x.Name, x => x.HireDate!))
{
    _instructorStore.Add(newInstructor);
    return RedirectToPage("./Index");
}

return Page();

TryUpdateModelAsync는 값 공급자를 사용하여 양식 본문, 쿼리 문자열 및 경로 데이터에서 데이터를 가져옵니다. TryUpdateModelAsync는 일반적으로

  • 과도한 게시를 방지하기 위해 컨트롤러 및 보기를 사용하여 Razor Pages 및 MVC 앱과 함께 사용됩니다.
  • 양식 데이터, 쿼리 문자열 및 경로 데이터에서 사용하지 않는 한 웹 API와 함께 사용되지 않습니다. JSON을 사용하는 웹 API 엔드포인트는 입력 포맷터를 사용하여 요청 본문을 개체로 역직렬화합니다.

자세한 내용은 TryUpdateModelAsync를 참조하세요.

[FromServices] 특성

이 특성의 이름은 데이터 원본을 지정하는 모델 바인딩 특성의 패턴을 따릅니다. 그러나 값 공급 기업의 바인딩 데이터에 대한 것은 아닙니다. 종속성 주입 컨테이너에서 형식의 인스턴스를 가져옵니다. 특정 메서드가 호출되는 경우에만 서비스가 필요할 때 생성자 주입에 대안을 제공하는 것이 목적입니다.

형식의 인스턴스가 종속성 주입 컨테이너에 등록되지 않았을 때 매개 변수를 바인딩하려고 시도하는 경우 앱이 예외를 throw합니다. 매개 변수를 선택 사항으로 만들려면 다음 방법 중 하나를 사용합니다.

  • 매개 변수를 null 허용으로 설정합니다.
  • 매개 변수의 기본값을 설정합니다.

null 허용 매개 변수의 경우 매개 변수에 액세스하기 전에 매개 변수가 null이 아닌지 확인합니다.

추가 리소스

이 문서는 모델 바인딩이 무엇인지, 작동 방법 및 해당 동작을 사용자 지정하는 방법을 설명합니다.

예제 코드 살펴보기 및 다운로드 (다운로드 방법). 다운로드 예제는 영역을 테스트하기 위한 기초적인 앱을 제공합니다.

모델 바인딩이란

컨트롤러 및 Razor Pages는 HTTP 요청에서 제공되는 데이터를 사용하여 작동합니다. 예를 들어 경로 데이터는 레코드 키를 제공할 수 있으며, 게시된 양식 필드는 모델의 속성에 대한 값을 제공할 수 있습니다. 이러한 각 값을 검색하고 문자열에서 .NET 형식으로 변환하도록 코드를 작성하는 것은 번거롭고 오류가 발생하기 쉽습니다. 모델 바인딩은 이 프로세스를 자동화합니다. 모델 바인딩 시스템:

  • 경로 데이터, 양식 필드 및 쿼리 문자열과 같은 다양한 원본의 데이터를 검색합니다.
  • 메서드 매개 변수 및 공용 속성에서 컨트롤러 및 Razor Pages에 데이터를 제공합니다.
  • 문자열 데이터를 .NET 형식으로 변환합니다.
  • 복합 형식의 속성을 업데이트합니다.

예시

다음 작업 메서드를 사용합니다.

[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)

앱은 이 URL로 요청을 수신합니다.

http://contoso.com/api/pets/2?DogsOnly=true

모델 바인딩은 라우팅 시스템이 작업 메서드를 선택한 후 다음 단계를 통해 진행됩니다.

  • GetById의 첫 번째 매개 변수, id라는 정수를 찾습니다.
  • HTTP 요청에서 사용 가능한 원본을 찾고 경로 데이터에서 id = "2"를 찾습니다.
  • 문자열 "2"를 정수 2로 변환합니다.
  • GetById의 다음 매개 변수, dogsOnly라는 부울을 찾습니다.
  • 원본을 찾고 쿼리 문자열에서 "DogsOnly=true"를 찾습니다. 이름 일치는 대/소문자를 구분하지 않습니다.
  • 문자열 "true"를 부울 true로 변환합니다.

그런 다음, 프레임워크는 id 매개 변수에 대해 2를 dogsOnly 매개 변수에 대해 true를 전달하는 GetById 메서드를 호출합니다.

이전 예제에서 모델 바인딩 대상은 간단한 형식인 메서드 매개 변수입니다. 대상은 복합 형식의 속성일 수도 있습니다. 각 속성이 성공적으로 바인딩된 후 모델 유효성 검사가 해당 속성에 대해 발생합니다. 모델에 바인딩되는 데이터의 레코드 및 모든 바인딩 또는 유효성 검사 오류는 ControllerBase.ModelState 또는 PageModel.ModelState에 저장됩니다. 이 프로세스가 성공되었는지 확인하기 위해 앱은 ModelState.IsValid 플래그를 확인합니다.

대상

모델 바인딩은 다음 종류의 대상에 대한 값을 찾으려고 합니다.

  • 요청이 라우팅되는 컨트롤러 작업 메서드의 매개 변수
  • 요청이 라우팅되는 Razor Pages 처리기 메서드의 매개 변수
  • 특성으로 지정되는 경우 컨트롤러 또는 PageModel 클래스의 공용 속성

[BindProperty] 특성

모델 바인딩이 해당 속성을 대상으로 하도록 컨트롤러의 공용 속성 또는 PageModel 클래스에 적용할 수 있습니다.

public class EditModel : InstructorsPageModel
{
    [BindProperty]
    public Instructor Instructor { get; set; }

[BindProperties] 특성

ASP.NET Core 2.1 이상에서 사용할 수 있습니다. 모델 바인딩이 클래스의 모든 공용 속성을 대상으로 하도록 컨트롤러 또는 PageModel 클래스에 적용할 수 있습니다.

[BindProperties(SupportsGet = true)]
public class CreateModel : InstructorsPageModel
{
    public Instructor Instructor { get; set; }

HTTP GET 요청에 대한 모델 바인딩

기본적으로 속성은 HTTP GET 요청에 대해 바인딩되지 않습니다. 일반적으로 GET 요청에 대해 필요한 것은 레코드 ID 매개 변수입니다. 레코드 ID는 데이터베이스에 있는 항목을 찾는 데 사용됩니다. 따라서 모델의 인스턴스를 포함하는 속성을 바인딩할 필요가 없습니다. 속성을 GET 요청의 데이터에 바인딩하려는 시나리오에서 SupportsGet 속성을 true로 설정합니다.

[BindProperty(Name = "ai_user", SupportsGet = true)]
public string ApplicationInsightsCookie { get; set; }

원본

기본적으로 모델 바인딩은 HTTP 요청의 다음 원본에서 키-값 쌍의 양식으로 데이터를 가져옵니다.

  1. 양식 필드
  2. 요청 본문([ApiController] 특성이 있는 컨트롤러의 경우)
  3. 경로 데이터
  4. 쿼리 문자열 매개 변수
  5. 업로드된 파일

각 대상 매개 변수 또는 속성의 경우, 소스가 이 목록에 표시된 순서대로 검사됩니다. 몇 가지 예외도 있습니다.

  • 경로 데이터 및 쿼리 문자열 값은 간단한 형식에만 사용됩니다.
  • 업로드된 파일은 IFormFile 또는 IEnumerable<IFormFile>을 구현하는 대상 유형에만 바인딩됩니다.

기본 소스가 올바르지 않으면 다음 특성 중 하나를 사용하여 소스를 지정합니다.

  • [FromQuery] - 쿼리 문자열에서 값을 가져옵니다.
  • [FromRoute] - 경로 데이터에서 값을 가져옵니다.
  • [FromForm] - 게시된 양식 필드에서 값을 가져옵니다.
  • [FromBody] - 요청 본문에서 값을 가져옵니다.
  • [FromHeader] - HTTP 헤더에서 값을 가져옵니다.

이러한 특성:

  • 다음 예제와 같이 모델 속성(모델 클래스가 아닌)에 개별적으로 추가됩니다.

    public class Instructor
    {
        public int ID { get; set; }
    
        [FromQuery(Name = "Note")]
        public string NoteFromQueryString { get; set; }
    
  • 필요에 따라 생성자에서 모델 이름 값을 허용합니다. 이 옵션은 속성 이름이 요청의 값과 일치하지 않는 경우에 제공됩니다. 예를 들어 요청의 값은 다음 예제와 같이 해당 이름에 하이픈이 있는 헤더일 수 있습니다.

    public void OnGet([FromHeader(Name = "Accept-Language")] string language)
    

[FromBody] 특성

매개 변수에 [FromBody] 특성을 적용하여 HTTP 요청의 본문에서 해당 속성을 채웁니다. ASP.NET Core 런타임은 본문을 읽을 책임을 입력 포맷터에 위임합니다. 입력 포맷터는 이 문서의 뒷부분에 설명되어 있습니다.

[FromBody]이(가) 복합 형식 매개 변수에 적용되는 경우 해당 속성에 적용된 모든 바인딩 소스 특성은 무시됩니다. 예를 들어, 다음 Create 작업은 해당 pet 매개 변수가 본문에서 채워지도록 지정합니다.

public ActionResult<Pet> Create([FromBody] Pet pet)

Pet 클래스는 Breed 속성이 쿼리 문자열 매개 변수에서 채워지도록 지정합니다.

public class Pet
{
    public string Name { get; set; }

    [FromQuery] // Attribute is ignored.
    public string Breed { get; set; }
}

앞의 예에서:

  • [FromQuery] 특성은 무시됩니다.
  • Breed 속성은 쿼리 문자열 매개 변수에서 채워지지 않습니다.

입력 포맷터는 본문만 읽고 바인딩 소스 특성은 인식하지 않습니다. 본문에 적절한 값이 있는 경우 해당 값은 Breed 속성을 채우는 데 사용됩니다.

작업 메서드당 둘 이상의 매개 변수에 [FromBody]를 적용하지 마십시오. 입력 포맷터에서 요청 스트림을 읽으면 더 이상 다른 [FromBody] 매개 변수를 바인딩하기 위해 다시 읽을 수 없습니다.

추가 원본

원본 데이터는 값 공급 기업에 의해 모델 바인딩 시스템에 제공됩니다. 다른 원본에서 모델 바인딩에 대한 데이터를 가져오는 사용자 지정 값 공급 기업을 작성 및 등록할 수 있습니다. 예를 들어 쿠키 또는 세션 상태의 데이터를 원할 수 있습니다. 새 원본에서 데이터를 가져오려면 다음을 수행합니다.

  • IValueProvider를 구현하는 클래스를 만듭니다.
  • IValueProviderFactory를 구현하는 클래스를 만듭니다.
  • Startup.ConfigureServices에서 팩터리 클래스를 등록합니다.

샘플 앱은 쿠키에서 값을 가져오는 값 공급 기업팩터리 예제를 포함합니다. 다음은 Startup.ConfigureServices의 등록 코드입니다.

services.AddRazorPages()
    .AddMvcOptions(options =>
{
    options.ValueProviderFactories.Add(new CookieValueProviderFactory());
    options.ModelMetadataDetailsProviders.Add(
        new ExcludeBindingMetadataProvider(typeof(System.Version)));
    options.ModelMetadataDetailsProviders.Add(
        new SuppressChildValidationMetadataProvider(typeof(System.Guid)));
})
.AddXmlSerializerFormatters();

표시된 코드는 사용자 지정 값 공급 기업을 모든 기본 제공 값 공급 기업 다음에 배치합니다. 목록에서 첫 번째로 지정하려면 Add 대신에 Insert(0, new CookieValueProviderFactory())를 호출합니다.

모델 속성에 대한 원본 없음

기본적으로 모델 속성에 대한 값이 없으면 모델 상태 오류가 생성되지 않습니다. 속성은 Null 또는 기본값으로 설정됩니다.

  • Nullable 단순 형식은 null로 설정됩니다.
  • Null을 허용하지 않는 값 형식은 default(T)로 설정됩니다. 예를 들어 매개 변수 int id는 0으로 설정됩니다.
  • 복합 형식의 경우 모델 바인딩은 속성을 설정하지 않고 기본 생성자를 사용하여 인스턴스를 만듭니다.
  • 배열은 byte[] 배열이 null로 설정되는 점을 제외하고 Array.Empty<T>()로 설정됩니다.

모델 속성에 대한 양식 필드에 아무것도 없을 때 모델 상태가 무효화되어야 하는 경우 [BindRequired] 속성을 사용합니다.

[BindRequired] 동작은 요청 본문의 JSON 또는 XML 데이터가 아니라 게시된 양식 데이터의 모델 바인딩에 적용됩니다. 요청 본문 데이터는 입력 포맷터에서 처리됩니다.

형식 변환 오류

원본이 있지만 대상 형식으로 변환될 수 없는 경우 모델 상태는 잘못된 것으로 플래그가 지정됩니다. 이전 섹션에서 설명한 것처럼 대상 매개 변수 또는 속성은 Null 또는 기본값으로 설정됩니다.

[ApiController] 특성이 있는 API 컨트롤러에서 잘못된 모델 상태로 자동 HTTP 400 응답이 발생합니다.

Razor Page에서 페이지를 오류 메시지와 함께 다시 표시합니다.

public IActionResult OnPost()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _instructorsInMemoryStore.Add(Instructor);
    return RedirectToPage("./Index");
}

클라이언트 쪽 유효성 검사는 Razor Pages 양식으로 제출될 수 있는 대부분의 잘못된 데이터를 catch합니다. 이 유효성 검사는 위의 강조 표시된 코드를 트리거하기 어렵게 만듭니다. 샘플 앱은 Hire Date 필드에 잘못된 데이터를 배치하고 양식을 제출하는 잘못된 데이터로 제출 단추를 포함합니다. 이 단추는 데이터 변환 오류가 발생하는 경우 페이지를 다시 표시하기 위해 코드가 작동하는 방식을 보여 줍니다.

위의 코드로 페이지가 다시 표시될 때 잘못된 입력은 양식 필드에 표시되지 않습니다. 이는 모델 속성이 Null 또는 기본값으로 설정되었기 때문입니다. 잘못된 입력은 오류 메시지에 표시됩니다. 그러나 양식 필드에 잘못된 데이터를 다시 표시하려는 경우 모델 속성을 문자열로 만들고 데이터 변환을 수동으로 수행하는 것이 좋습니다.

형식 변환 오류를 모델 상태 오류로 만들려 하지 않는 경우 동일한 전략을 권장합니다. 이 경우 모델 속성을 문자열로 만듭니다.

단순 형식

모델 바인더에서 원본 문자열을 변환할 수 있는 단순 형식은 다음을 포함합니다.

복합 형식

복합 형식에 공용 기본 생성자와 바인딩할 공용 쓰기 가능 속성이 있어야 합니다. 모델 바인딩이 발생하면 클래스는 공용 기본 생성자를 사용하여 인스턴스화됩니다.

복합 형식의 각 속성의 경우 모델 바인딩은 이름 패턴prefix.property_name에 대한 원본을 찾습니다. 아무것도 없는 경우 접두사 없이 property_name만을 찾습니다.

매개 변수에 대한 바인딩의 경우 접두사는 매개 변수 이름입니다. PageModel 공용 속성에 대한 바인딩의 경우 접두사는 공용 속성 이름입니다. 일부 특성에는 매개 변수의 기본 사용 또는 속성 이름을 재정의할 수 있도록 하는 Prefix 속성이 있습니다.

예를 들어 복합 형식이 다음 Instructor 클래스라고 가정합니다.

public class Instructor
{
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
}

접두사 = 매개 변수 이름

바인딩될 모델이 instructorToUpdate라는 매개 변수인 경우:

public IActionResult OnPost(int? id, Instructor instructorToUpdate)

모델 바인딩은 키 instructorToUpdate.ID에 대한 원본을 찾아 시작합니다. 없는 경우 접두사 없이 ID를 찾습니다.

접두사 = 속성 이름

바인딩될 모델이 컨트롤러 또는 PageModel 클래스의 Instructor라는 속성인 경우:

[BindProperty]
public Instructor Instructor { get; set; }

모델 바인딩은 키 Instructor.ID에 대한 원본을 찾아 시작합니다. 없는 경우 접두사 없이 ID를 찾습니다.

사용자 지정 접두사

바인딩될 모델이 instructorToUpdate라는 매개 변수이고 Bind 특성이 접두사로 Instructor를 지정하는 경우:

public IActionResult OnPost(
    int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)

모델 바인딩은 키 Instructor.ID에 대한 원본을 찾아 시작합니다. 없는 경우 접두사 없이 ID를 찾습니다.

복합 형식 대상에 대한 특성

여러 기본 제공 특성은 복합 형식의 모델 바인딩을 제어할 수 있습니다.

  • [Bind]
  • [BindRequired]
  • [BindNever]

Warning

이러한 특성은 게시된 양식 데이터가 값의 원본일 때 모델 바인딩에 영향을 줍니다. 게시된 JSON 및 XML 요청 본문을 처리하는 입력 포맷터에는 영향을 주지 않습니다. 입력 포맷터는 이 문서의 뒷부분에 설명되어 있습니다.

[Bind] 특성

클래스 또는 메서드 매개 변수에 적용될 수 있습니다. 모델 바인딩에 포함되어야 하는 모델의 속성을 지정합니다. [Bind]는 입력 포맷터에 영향을 주지 않습니다.

다음 예제에서 모든 처리기 또는 작업 메서드가 호출될 때 지정된 속성의 Instructor 모델만 바인딩됩니다.

[Bind("LastName,FirstMidName,HireDate")]
public class Instructor

다음 예제에서 OnPost 메서드가 호출될 때 지정된 속성의 Instructor 모델만 바인딩됩니다.

[HttpPost]
public IActionResult OnPost([Bind("LastName,FirstMidName,HireDate")] Instructor instructor)

[Bind] 특성은 만들기 시나리오에서 초과 게시를 방지하는 데 사용될 수 있습니다. 제외된 속성은 변경되지 않은 채로 남겨지는 대신 Null 또는 기본값으로 설정되기 때문에 편집 시나리오에서 잘 작동하지 않습니다. 오버포스트 방지를 위해 특성 대신 [Bind] 보기 모델을 사용하는 것이 좋습니다. 자세한 내용은 초과 게시에 대한 보안 정보를 참조하세요.

[ModelBinder] 특성

ModelBinderAttribute는 형식, 속성 또는 매개 변수에 적용할 수 있습니다. 특정 인스턴스 또는 형식을 바인딩하는 데 사용되는 모델 바인더의 형식을 지정할 수 있습니다. 예시:

[HttpPost]
public IActionResult OnPost([ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)

[ModelBinder] 특성은 모델 바인딩될 때 속성 또는 매개 변수의 이름을 변경하는 데 사용할 수도 있습니다.

public class Instructor
{
    [ModelBinder(Name = "instructor_id")]
    public string Id { get; set; }

    public string Name { get; set; }
}

[BindRequired] 특성

메서드 매개 변수가 아닌 모델 속성에만 적용될 수 있습니다. 모델의 속성에 대한 바인딩이 발생할 수 없는 경우 모델 바인딩이 모델 상태 오류를 추가하도록 합니다. 예를 들어 다음과 같습니다.

public class InstructorWithCollection
{
    public int ID { get; set; }

    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    [Display(Name = "Hire Date")]
    [BindRequired]
    public DateTime HireDate { get; set; }

모델 유효성 검사에서 [Required] 특성의 설명을 참조하세요.

[BindNever] 특성

메서드 매개 변수가 아닌 모델 속성에만 적용될 수 있습니다. 모델 바인딩이 모델의 속성을 설정하는 것을 방지합니다. 예를 들어 다음과 같습니다.

public class InstructorWithDictionary
{
    [BindNever]
    public int ID { get; set; }

컬렉션

단순 형식의 컬렉션인 대상의 경우 모델 바인딩은 parameter_name 또는 property_name에 대한 일치 항목을 찾습니다. 일치하는 항목이 없는 경우 접두사 없이 지원되는 양식 중 하나를 찾습니다. 예시:

  • 바인딩되는 매개 변수가 selectedCourses라는 배열이라고 가정합니다.

    public IActionResult OnPost(int? id, int[] selectedCourses)
    
  • 양식 또는 쿼리 문자열 데이터는 다음 형식 중 하나일 수 있습니다.

    selectedCourses=1050&selectedCourses=2000 
    
    selectedCourses[0]=1050&selectedCourses[1]=2000
    
    [0]=1050&[1]=2000
    
    selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
    
    [a]=1050&[b]=2000&index=a&index=b
    

    index 또는 Index라는 이름의 매개 변수 또는 속성이 컬렉션 값에 인접한 경우에는 바인딩하지 않습니다. 모델 바인딩은 index를 컬렉션의 인덱스로 사용하려고 시도하며 이로 인해 바인딩이 잘못될 수 있습니다. 다음 작업을 예로 들 수 있습니다.

    public IActionResult Post(string index, List<Product> products)
    

    이전 코드에서 index 쿼리 문자열 매개 변수는 index 메서드 매개 변수에 바인딩하며 제품 컬렉션을 바인딩하는 데도 사용합니다. index 매개 변수 이름을 변경하거나 모델 바인딩 특성을 사용하여 바인딩을 구성하면 다음 문제가 발생하지 않습니다.

    public IActionResult Post(string productIndex, List<Product> products)
    
  • 다음 형식은 양식 데이터에서만 지원됩니다.

    selectedCourses[]=1050&selectedCourses[]=2000
    
  • 앞의 모든 예제 형식의 경우 모델 바인딩은 두 항목의 배열을 selectedCourses 매개 변수에 전달합니다.

    • selectedCourses[0]=1050
    • selectedCourses[1]=2000

    아래 첨자 숫자(... [0] ... [1] ...)를 사용하는 데이터 형식은 0부터 시작하여 순차적으로 번호가 매겨지는지 확인해야 합니다. 아래 첨자 번호에 간격이 있는 경우 간격 뒤에 있는 모든 항목은 무시됩니다. 예를 들어 아래 첨자가 0과 1 대신 0과 2인 경우 두 번째 항목은 무시됩니다.

사전

Dictionary 대상의 경우 모델 바인딩은 parameter_name 또는 property_name에 대한 일치 항목을 찾습니다. 일치하는 항목이 없는 경우 접두사 없이 지원되는 양식 중 하나를 찾습니다. 예시:

  • 대상 매개 변수가 selectedCourses라는 Dictionary<int, string>라고 가정합니다.

    public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
    
  • 게시된 양식 또는 쿼리 문자열 데이터는 다음 예제 중 하나와 같을 수 있습니다.

    selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
    
    [1050]=Chemistry&selectedCourses[2000]=Economics
    
    selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry&
    selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
    
    [0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
    
  • 앞의 모든 예제 형식의 경우 모델 바인딩은 두 항목의 사전을 selectedCourses 매개 변수에 전달합니다.

    • selectedCourses["1050"]="Chemistry"
    • selectedCourses["2000"]="Economics"

생성자 바인딩 및 레코드 형식

모델 바인딩을 사용하려면 복합 형식에 매개 변수가 없는 생성자가 있어야 합니다. System.Text.JsonNewtonsoft.Json 기반 입력 포맷터는 모두 매개 변수가 없는 생성자가 없는 클래스의 역직렬화를 지원합니다.

C# 9에서는 네트워크를 통해 데이터를 간략하게 표현할 수 있는 좋은 방법인 레코드 형식을 소개합니다. ASP.NET Core는 단일 생성자를 사용하여 모델 바인딩 및 레코드 형식 유효성 검사에 대한 지원을 추가합니다.

public record Person([Required] string Name, [Range(0, 150)] int Age, [BindNever] int Id);

public class PersonController
{
   public IActionResult Index() => View();

   [HttpPost]
   public IActionResult Index(Person person)
   {
       ...
   }
}

Person/Index.cshtml:

@model Person

<label>Name: <input asp-for="Name" /></label>
...
<label>Age: <input asp-for="Age" /></label>

레코드 형식의 유효성을 검사할 때 런타임에서는 속성이 아닌 매개 변수에 대한 바인딩 및 유효성 검사 메타데이터를 검색합니다.

프레임워크는 레코드 형식에 대한 바인딩 및 유효성 검사를 허용합니다.

public record Person([Required] string Name, [Range(0, 100)] int Age);

위의 작업을 수행하려면 형식은 다음과 같아야 합니다.

  • 레코드 형식이어야 합니다.
  • 공용 생성자가 하나만 있어야 합니다.
  • 이름 및 형식이 같은 속성을 포함하는 매개 변수를 포함합니다. 경우에 따라 이름이 달라져서는 안 됩니다.

매개 변수가 없는 생성자가 없는 POCO

매개 변수가 없는 생성자가 없는 POCO는 바인딩할 수 없습니다.

다음 코드에서는 형식에 매개 변수가 없는 생성자가 있어야 한다는 예외가 발생합니다.

public class Person(string Name)

public record Person([Required] string Name, [Range(0, 100)] int Age)
{
   public Person(string Name) : this (Name, 0);
}

수동으로 작성된 생성자를 사용하는 레코드 형식

기본 생성자가 작동하는 것처럼 보이는 수동으로 작성된 생성자를 사용하는 레코드 형식

public record Person
{
   public Person([Required] string Name, [Range(0, 100)] int Age) => (this.Name, this.Age) = (Name, Age);

   public string Name { get; set; }
   public int Age { get; set; }
}

레코드 형식, 유효성 검사 및 바인딩 메타데이터

레코드 형식의 경우 매개 변수에 대한 유효성 검사 및 바인딩 메타데이터가 사용됩니다. 속성의 메타데이터는 무시됩니다.

public record Person (string Name, int Age)
{
   [BindProperty(Name = "SomeName")] // This does not get used
   [Required] // This does not get used
   public string Name { get; init; }
}

유효성 검사 및 메타데이터

유효성 검사는 매개 변수에 대한 메타데이터를 사용하지만 속성을 사용하여 값을 읽습니다. 기본 생성자를 사용하는 일반적인 경우 두 생성자는 동일합니다. 그러나 이를 무력화하는 방법은 다음과 같습니다.

public record Person([Required] string Name)
{
   private readonly string _name;
   public Name { get; init => _name = value ?? string.Empty; } // Now this property is never null. However this object could have been constructed as `new Person(null);`
}

TryUpdateModel은 레코드 형식의 매개 변수를 업데이트하지 않습니다.

public record Person(string Name)
{
   public int Age { get; set; }
}

var person = new Person("initial-name");
TryUpdateModel(person, ...);

이 경우 MVC는 다시 Name을 바인딩하지 않습니다. 그러나 Age를 업데이트할 수 있습니다.

모델 바인딩 경로 데이터 및 쿼리 문자열의 세계화 동작

ASP.NET Core 경로 값 공급자와 쿼리 문자열 값 공급자:

  • 값을 고정 문화권으로 처리합니다.
  • URL을 고정 문화권으로 간주합니다.

이와 대조적으로, 양식 데이터에서 가져온 값은 문화권을 구분하여 변환을 수행합니다. 이는 기본적으로 URL을 로캘 간에 공유할 수 있도록 설계되었습니다.

ASP.NET Core 경로 값 공급자와 쿼리 문자열 값 공급자가 문화권 구분 변환을 수행하도록 하려면 다음을 수행합니다.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
    {
        var index = options.ValueProviderFactories.IndexOf(
            options.ValueProviderFactories.OfType<QueryStringValueProviderFactory>().Single());
        options.ValueProviderFactories[index] = new CulturedQueryStringValueProviderFactory();
    });
}
public class CulturedQueryStringValueProviderFactory : IValueProviderFactory
{
    public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        var query = context.ActionContext.HttpContext.Request.Query;
        if (query != null && query.Count > 0)
        {
            var valueProvider = new QueryStringValueProvider(
                BindingSource.Query,
                query,
                CultureInfo.CurrentCulture);

            context.ValueProviders.Add(valueProvider);
        }

        return Task.CompletedTask;
    }
}

특수 데이터 형식

모델 바인딩이 처리할 수 있는 일부 특수 데이터 형식이 있습니다.

IFormFile 및 IFormFileCollection

HTTP 요청에 포함되는 업로드된 파일입니다. 또한 여러 파일에 대해 IEnumerable<IFormFile>이 지원됩니다.

CancellationToken

작업은 필요에 따라 CancellationToken을 매개 변수로 바인딩할 수 있습니다. 그러면 HTTP 요청을 기반으로 하는 연결이 중단될 때 신호를 보내는 RequestAborted가 바인딩됩니다. 작업은 이 매개 변수를 사용하여 컨트롤러 작업의 일부로 실행되는 장기 실행 비동기 작업을 취소할 수 있습니다.

FormCollection

게시된 양식 데이터에서 모든 값을 검색하는 데 사용됩니다.

입력 포맷터

요청 본문의 데이터는 JSON, XML 또는 일부 다른 형식일 수 있습니다. 이 데이터를 구문 분석하기 위해 모델 바인딩은 특정 콘텐츠 유형을 처리하도록 구성된 입력 포맷터를 사용합니다. 기본적으로 ASP.NET Core는 JSON 데이터를 처리하기 위한 JSON 기반 입력 포맷터를 포함합니다. 다른 콘텐츠 형식에 대해 다른 포맷터를 추가할 수 있습니다.

ASP.NET Core는 Consumes 특성을 기반으로 입력 포맷터를 선택합니다. 특성이 없는 경우 Content-Type 헤더를 사용합니다.

기본 제공 XML 입력 포맷터를 사용하려면 다음을 수행합니다.

  • Microsoft.AspNetCore.Mvc.Formatters.Xml NuGet 패키지를 설치합니다.

  • Startup.ConfigureServices에서 AddXmlSerializerFormatters 또는 AddXmlDataContractSerializerFormatters를 호출합니다.

    services.AddRazorPages()
        .AddMvcOptions(options =>
    {
        options.ValueProviderFactories.Add(new CookieValueProviderFactory());
        options.ModelMetadataDetailsProviders.Add(
            new ExcludeBindingMetadataProvider(typeof(System.Version)));
        options.ModelMetadataDetailsProviders.Add(
            new SuppressChildValidationMetadataProvider(typeof(System.Guid)));
    })
    .AddXmlSerializerFormatters();
    
  • 요청 본문에서 XML을 필요로 하는 컨트롤러 클래스 또는 작업 메서드에 Consumes 특성을 적용합니다.

    [HttpPost]
    [Consumes("application/xml")]
    public ActionResult<Pet> Create(Pet pet)
    

    자세한 내용은 XML Serialization 소개를 참조하세요.

입력 포맷터를 사용하여 모델 바인딩 사용자 지정

입력 포맷터는 요청 본문에서 데이터를 읽기 위한 모든 작업을 수행합니다. 이 프로세스를 사용자 지정하려면 입력 포맷터에서 사용하는 API를 구성합니다. 이 섹션에서는 ObjectId라는 사용자 지정 형식을 이해하기 위해 System.Text.Json 기반 입력 포맷터를 사용자 지정하는 방법을 설명합니다.

Id라는 사용자 지정 ObjectId 속성을 포함하는 다음 모델을 고려합니다.

public class ModelWithObjectId
{
    public ObjectId Id { get; set; }
}

System.Text.Json를 사용할 때 모델 바인딩 프로세스를 사용자 지정하려면 JsonConverter<T>에서 파생된 클래스를 만듭니다.

internal class ObjectIdConverter : JsonConverter<ObjectId>
{
    public override ObjectId Read(
        ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return new ObjectId(JsonSerializer.Deserialize<int>(ref reader, options));
    }

    public override void Write(
        Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options)
    {
        writer.WriteNumberValue(value.Id);
    }
}

사용자 지정 변환기를 사용하려면 형식에 JsonConverterAttribute 특성을 적용합니다. 다음 예제에서 ObjectId 형식은 ObjectIdConverter를 사용자 지정 변환기로 사용하여 구성됩니다.

[JsonConverter(typeof(ObjectIdConverter))]
public struct ObjectId
{
    public ObjectId(int id) =>
        Id = id;

    public int Id { get; }
}

자세한 내용은 사용자 지정 변환기를 작성하는 방법을 참조하세요.

모델 바인딩에서 지정된 형식 제외

모델 바인딩 및 유효성 검사 시스템의 동작은 .에 의해 ModelMetadata구동됩니다. MvcOptions.ModelMetadataDetailsProviders에 세부 정보 공급 기업을 추가하여 ModelMetadata를 사용자 지정할 수 있습니다. 기본 제공 세부 정보 공급 기업을 지정된 형식에 대한 모델 바인딩 또는 유효성 검사를 비활성화하기 위해 사용할 수 있습니다.

지정된 형식의 모든 모델에 대한 모델 바인딩을 비활성화하려면 Startup.ConfigureServices에서 ExcludeBindingMetadataProvider를 추가합니다. 예를 들어 System.Version 형식의 모든 모델에 대한 모델 바인딩을 비활성화하려면:

services.AddRazorPages()
    .AddMvcOptions(options =>
{
    options.ValueProviderFactories.Add(new CookieValueProviderFactory());
    options.ModelMetadataDetailsProviders.Add(
        new ExcludeBindingMetadataProvider(typeof(System.Version)));
    options.ModelMetadataDetailsProviders.Add(
        new SuppressChildValidationMetadataProvider(typeof(System.Guid)));
})
.AddXmlSerializerFormatters();

지정된 형식의 속성에 대한 유효성 검사를 비활성화하려면 Startup.ConfigureServices에서 SuppressChildValidationMetadataProvider를 추가합니다. 예를 들어 System.Guid 형식의 속성에 대한 유효성 검사를 비활성화하려면:

services.AddRazorPages()
    .AddMvcOptions(options =>
{
    options.ValueProviderFactories.Add(new CookieValueProviderFactory());
    options.ModelMetadataDetailsProviders.Add(
        new ExcludeBindingMetadataProvider(typeof(System.Version)));
    options.ModelMetadataDetailsProviders.Add(
        new SuppressChildValidationMetadataProvider(typeof(System.Guid)));
})
.AddXmlSerializerFormatters();

사용자 지정 모델 바인더

사용자 지정 모델 바인더를 작성하고 지정된 대상에 대해 선택하도록 [ModelBinder] 특성을 사용하여 모델 바인딩을 확장할 수 있습니다. 사용자 모델 바인딩에 대해 자세히 알아보세요.

수동 모델 바인딩

TryUpdateModelAsync 메서드를 사용하여 모델 바인딩을 수동으로 호출할 수 있습니다. 메서드는 ControllerBasePageModel 클래스에서 정의됩니다. 메서드 오버로드를 통해 사용할 접두사 및 값 공급 기업을 지정할 수 있습니다. 모델 바인딩이 실패하는 경우 메서드는 false를 반환합니다. 예를 들어 다음과 같습니다.

if (await TryUpdateModelAsync<InstructorWithCollection>(
    newInstructor,
    "Instructor",
    i => i.FirstMidName, i => i.LastName, i => i.HireDate))
{
    _instructorsInMemoryStore.Add(newInstructor);
    return RedirectToPage("./Index");
}
PopulateAssignedCourseData(newInstructor);
return Page();

TryUpdateModelAsync는 값 공급자를 사용하여 양식 본문, 쿼리 문자열 및 경로 데이터에서 데이터를 가져옵니다. TryUpdateModelAsync는 일반적으로

  • 과도한 게시를 방지하기 위해 컨트롤러 및 보기를 사용하여 Razor Pages 및 MVC 앱과 함께 사용됩니다.
  • 양식 데이터, 쿼리 문자열 및 경로 데이터에서 사용하지 않는 한 웹 API와 함께 사용되지 않습니다. JSON을 사용하는 웹 API 엔드포인트는 입력 포맷터를 사용하여 요청 본문을 개체로 역직렬화합니다.

자세한 내용은 TryUpdateModelAsync를 참조하세요.

[FromServices] 특성

이 특성의 이름은 데이터 원본을 지정하는 모델 바인딩 특성의 패턴을 따릅니다. 그러나 값 공급 기업의 바인딩 데이터에 대한 것은 아닙니다. 종속성 주입 컨테이너에서 형식의 인스턴스를 가져옵니다. 특정 메서드가 호출되는 경우에만 서비스가 필요할 때 생성자 주입에 대안을 제공하는 것이 목적입니다.

추가 리소스