.NET Core WebApi 에서 다 중 데이터 바 인 딩 인 스 턴 스 코드 를 어떻게 실현 합 니까?

.NET Core 란 무엇 입 니까?
2014 년 Xamarin 과 마이크로소프트 가.NET 재단 을 시작 하면 서 마이크로소프트 는 2014 년 11 월.NET 프레임 워 크 소스 코드 를 개방 했다.닷 넷 오픈 소스 재단 의 통합 기획 으로 닷 넷 코어 가 탄생 했다.즉.NET Core Framework 는.NET Framework 를 참고 하여 재 개발 한.NET 실현 이 고,모 노 는.NET Framework 의 개 원 된 크로스 플랫폼 의 실현 이다.
본 고 는 주로.NET Core WebApi 다 중 데이터 바 인 딩 에 관 한 내용 을 소개 하 였 으 며,공유 하여 여러분 께 참고 학습 을 제공 하 였 습 니 다.다음은 더 이상 말씀 드 리 지 않 겠 습 니 다.상세 한 소 개 를 해 보 겠 습 니 다.
무엇이 다 중 데이터 바 인 딩 입 니까?
ASP.NET Core WebApi 에서 데이터 바 인 딩 메커니즘(Data Binding)이 바 인 딩 요청 파 라 메 터 를 담당 하 는 것 을 잘 알 고 있 습 니 다.보통 대부분의 데이터 바 인 딩 은 기본 데이터 바 인 딩 기(Binder)에서 정상적으로 진행 되 지만 지원 되 지 않 는 경우 도 있 습 니 다.예 를 들 어 다 중 데이터 바 인 딩 등 입 니 다.다 중 데이터 바 인 딩(polymorphic data binding)이란 요청 매개 변 수 는 하위 클래스 대상 의 JSon 문자열 이 고 action 에 서 는 부모 클래스 형식의 변 수 를 정의 합 니 다.기본 적 인 상황 에서 ASP.NET Core WebApi 는 다 중 데이터 바 인 딩 을 지원 하지 않 아 데 이 터 를 잃 어 버 릴 수 있 습 니 다.
아래 그림 을 예 로 들다

Person 류 는 아버지 류 이 고 Doctor 류 와 Student 류 는 Person 류 의 파생 류 이다.Doctor 클래스 에 있 는 HospitalName 속성,Student 에 있 는 SchoolName 속성 입 니 다.
허난 성
PeopleController 에 Add api 를 추가 하고 효 과 를 볼 수 있 도록 요청 데 이 터 를 직접 되 돌려 줍 니 다.

[Route("api/people")]
public class PeopleController : Controller
{
 [HttpPost]
 [Route("")]
 public List<Person> Add([FromBody]List<Person> people)
 {
 return people;
 }
}
여기 서 우 리 는 Postman 을 사용 하여 이 api 를 요청 합 니 다.요청 한 Content-Type 은 application/json 입 니 다.요청 한 Body 내용 은 다음 과 같 습 니 다.

[{
 firstName: 'Mike',
 lastName: 'Li'
}, {
 firstName: 'Stephie',
 lastName: 'Wang',
 schoolName: 'No.15 Middle School'
}, {
 firstName: 'Jacky',
 lastName: 'Chen',
 hospitalName: 'Center Hospital'
}]
요청 한 반환 내용

[
 {
 "FirstName": "Mike",
 "LastName": "Li"
 },
 {
 "FirstName": "Stephie",
 "LastName": "Wang"
 },
 {
 "FirstName": "Jacky",
 "LastName": "Chen"
 }
]
반환 결 과 는 우리 가 원 하 는 결과 와 다 릅 니 다.Student 가 가지 고 있 는 SchoolName 속성 과 Doctor 가 가지 고 있 는 HospitalName 속성 을 모두 잃 어 버 렸 습 니 다.
현재 프로젝트 디 버 깅 모드 를 시작 합 니 다.Postman 을 다시 사용 하여 요청 한 결 과 는 다음 과 같 습 니 다.

People 집합 에 3 개의 People 유형 을 저장 하 는 대상 은 우리 가 원 하 는 Student 유형 대상 과 Doctor 유형 대상 이 나타 나 지 않 았 습 니 다.이 는.NET Core WebApi 는 기본적으로 다 중 데이터 바 인 딩 을 지원 하지 않 습 니 다.부모 유형 변 수 를 사용 하여 데 이 터 를 받 으 면 Data Binding 은 파생 대상 이 아 닌 부모 유형 대상 만 사례 화하 여 속성 을 잃 게 됩 니 다.
사용자 정의 JSonConverter 로 다 중 데이터 바 인 딩 실현
JSonConverter 는 JSon.NET 의 한 종류 로 주로 JSon 대상 의 서열 화 와 반 서열 화 를 책임 진다.
먼저,우 리 는 일반적인 유형의 JSonCreationConverter 를 만 들 고 JSonConverter 류 를 계승 하 였 습 니 다.코드 는 다음 과 같 습 니 다.

public abstract class JsonCreationConverter<T> : JsonConverter
{
 public override bool CanWrite
 {
 get
 {
  return false;
 }
 }

 protected abstract T Create(Type objectType, JObject jObject);

 public override bool CanConvert(Type objectType)
 {
 return typeof(T).IsAssignableFrom(objectType);
 }


 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
 {
 if (reader == null) throw new ArgumentNullException("reader");
 if (serializer == null) throw new ArgumentNullException("serializer");
 if (reader.TokenType == JsonToken.Null)
  return null;

 JObject jObject = JObject.Load(reader);
 T target = Create(objectType, jObject);
 serializer.Populate(jObject.CreateReader(), target);
 return target;
 }

 public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
 {
 throw new NotImplementedException();
 }
}
그 중에서 우 리 는 추상 적 인 방법 Create 를 추 가 했 습 니 다.이 방법 은 JSon 문자열 의 내용 에 따라 범 형 대상 을 되 돌려 줍 니 다.여 기 는 현재 범 형 유형의 대상 도 되 돌려 줄 수 있 고 현재 범 형 유형의 파생 대상 도 되 돌려 줄 수 있 습 니 다.JObject 는 JSon.NET 의 JSon 문자열 리더 로 JSon 문자열 의 속성 값 을 읽 습 니 다.
또한 저 희 는 ReadJSon 방법 도 복 사 했 습 니 다.ReadJSon 에서 저 희 는 Create 방법 으로 현재 범 형 대상 이나 현재 범 형 류 의 파생 대상 을 가 져 옵 니 다(JSon.NET 에서 기본 적 인 KeyValuePair Converter 는 현재 매개 변수 유형 대상 을 직접 예화 합 니 다.이것 은 기본 적 으로 다 중 데이터 바 인 딩 을 지원 하지 않 는 주요 원인 입 니 다).serializer.Popluate 방법의 역할 은 JSon 문자열 의 내용 을 대상(현재 범 형 대상 또는 현재 범 형 클래스 의 파생 대상)에 비 추 는 대응 속성 입 니 다.
여 기 는 우리 가 JSon 만 읽 어야 하기 때문에 Write JSon 의 방법 을 우 리 는 실현 할 필요 가 없습니다.CanWrite 속성 은 우리 도 강제로 False 로 돌 아 왔 습 니 다.
두 번 째 단 계 는 PersonJSonConverter 클래스 를 만 들 었 습 니 다.JSonCreationConverter을 계 승 했 습 니 다.그 코드 는 다음 과 같 습 니 다.

public class PersonJsonConverter : JsonCreationConverter<Person>
{
 protected override Person Create(Type objectType, JObject jObject)
 {
 if (jObject == null) throw new ArgumentNullException("jObject");

 if (jObject["schoolName"] != null)
 {
  return new Student();
 }
 else if (jObject["hospitalName"] != null)
 {
  return new Doctor();
 }
 else
 {
  return new Person();
 }
 }
}
이 클래스 에서 Create 방법 을 복 사 했 습 니 다.JObject 를 사용 하여 JSon 문자열 에 있 는 속성 을 가 져 옵 니 다.
  • 문자열 에 schoolName 속성 이 포함 되 어 있 으 면 새로운 Student 대상
  • 을 되 돌려 줍 니 다.
  • 문자열 에 hospitalName 속성 이 포함 되 어 있 으 면 새로운 Doctor 대상
  • 을 되 돌려 줍 니 다.
  • 그렇지 않 으 면 새로운 Person 대상 으로 돌아 갑 니 다
  • 마지막 으로,우 리 는 Person 클래스 에서 특성 표시 Person 클래스 를 사용 하여 Person JSonConverter 를 사용 하여 JSon 서열 화 와 반 서열 화 를 변환 합 니 다.
    
    [JsonConverter(typeof(PersonJsonConverter))]
    public class Person
    {
     public string FirstName { get; set; }
    
     public string LastName { get; set; }
    }
    현재 디 버 깅 모드 로 프로그램 을 시작 한 다음 Postman 을 사용 하여 현재 api 를 요청 합 니 다.
     
    people 집합 에 올 바 르 게 연 결 된 파생 자 유형 대상 을 발견 할 수 있 습 니 다.최종 Postman 에서 우 리 는 다음 과 같은 응답 결 과 를 얻 었 습 니 다.
    
    [
     {
      "FirstName": "Mike",
      "LastName": "Li"
     },
     {
      "SchoolName": "No.15 Middle School",
      "FirstName": "Stephie",
      "LastName": "Wang"
     },
     {
      "HospitalName": "Center Hospital",
      "FirstName": "Jacky",
      "LastName": "Chen"
     }
    ]
    이로써 다 중 데이터 바 인 딩 에 성공 하 였 습 니 다.
    미주 알 고 주 알 캐묻다
    왜 PersonJSonConverter 류 를 추가 하여 다 중 바 인 딩 이 이 루어 졌 습 니까?
    MVC 코어 와 제 이 슨 닷 넷 의 코드 를 검토 해 봅 시다.
    일단 MvcCoreMvcOptionsSetup 코드 를 볼 게 요.
    
    public class MvcCoreMvcOptionsSetup : IConfigureOptions<MvcOptions>
    {
     private readonly IHttpRequestStreamReaderFactory _readerFactory;
     private readonly ILoggerFactory _loggerFactory;
    
     ......
      
     public void Configure(MvcOptions options)
     {
      options.ModelBinderProviders.Add(new BinderTypeModelBinderProvider());
      options.ModelBinderProviders.Add(new ServicesModelBinderProvider());
      options.ModelBinderProviders.Add(new BodyModelBinderProvider(options.InputFormatters, _readerFactory, _loggerFactory, options));
      ......
     }
    
     ......
     
    }
    MvcCoreMvcOptionsSetup 클래스 의 Configure 방법 은 기본 데이터 바 인 딩 사용 Provider 목록 을 설정 합 니 다.
    api 매개 변수 가[FromBody]로 표 시 될 때 Body ModelBinder Provider 는 Body ModelBinder 대상 을 예화 하여 이 매개 변 수 를 처리 하고 데이터 바 인 딩 을 시도 합 니 다.
    Body Model Binder 클래스 에는 Bind Model Async 방법 이 있 습 니 다.이름 의 글자 그대로 우 리 는 이 방법 이 데 이 터 를 연결 하 는 데 쓰 인 다 는 것 을 잘 알 고 있 습 니 다.
    
    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
     if (bindingContext == null)
     {
      throw new ArgumentNullException(nameof(bindingContext));
     }
    
      ….
    
     var formatter = (IInputFormatter)null;
     for (var i = 0; i < _formatters.Count; i++)
     {
       if (_formatters[i].CanRead(formatterContext))
      {
       formatter = _formatters[i];
       _logger?.InputFormatterSelected(formatter, formatterContext);
       break;
      }
      else
      {
        logger?.InputFormatterRejected(_formatters[i], formatterContext);
      }
     }
    
     ……
    
     try
     {
      var result = await formatter.ReadAsync(formatterContext);
    
      ……
     }
     catch (Exception exception) when (exception is InputFormatterException || ShouldHandleException(formatter))
     {
      bindingContext.ModelState.AddModelError(modelBindingKey, exception, bindingContext.ModelMetadata);
     }
    }
    이 방법 에 서 는 IInputFormatter 대상 을 찾 아 데 이 터 를 연결 하려 고 합 니 다.이때 요청 한 Content-Type 은 application/json 이기 때문에 JSonInputFormatter 대상 을 사용 하여 데이터 바 인 딩 을 합 니 다.
    다음은 JSONInputFormatter 류 의 핵심 코드 를 살 펴 보 겠 습 니 다.
    
    public override async Task<InputFormatterResult> ReadRequestBodyAsync(
       InputFormatterContext context,
       Encoding encoding)
    {
     ......
    
     using (var streamReader = context.ReaderFactory(request.Body, encoding))
     {
      using (var jsonReader = new JsonTextReader(streamReader))
      {
       …
    
       object model;
       try
       {
        model = jsonSerializer.Deserialize(jsonReader, type);
       }
       finally
       {
        jsonSerializer.Error -= ErrorHandler;
        ReleaseJsonSerializer(jsonSerializer);
       }
    
       …
      }
     }
    }
    JSonInputFormatter 클래스 의 ReadRequestBody Async 방법 은 데이터 바 인 딩 을 담당 하 며,이 방법 에 서 는 JSon.NET 의 JSonSerializer 류 Deserialize 방법 을 사용 하여 역 직렬 화 를 진행 하 였 으 며,이 는 Mvc Core 의 바 텀 이 JSon.NET 을 직접 사용 하여 JSon 을 조작 한 다 는 것 을 의미한다.
    JSonSerializer 클래스 의 일부 핵심 코드
    
    public object Deserialize(JsonReader reader, Type objectType)
    {
     return DeserializeInternal(reader, objectType);
    }
    
    internal virtual object DeserializeInternal(JsonReader reader, Type objectType)
    {
     ……
    
     JsonSerializerInternalReader serializerReader = new JsonSerializerInternalReader(this);
     object value = serializerReader.Deserialize(traceJsonReader ?? reader, objectType, CheckAdditionalContent);
    
     ……
     return value;
    }
    JSonSerializer 는 JSonSerializer Internal Reader 류 의 Deserialize 방법 으로 JSon 문자열 의 내용 을 역 정렬 합 니 다.
    마지막 으로 JSonSerializer Internal Reader 의 일부 핵심 코드 를 살 펴 보 겠 습 니 다.
    
    public object Deserialize(JsonReader reader, Type objectType, bool checkAdditionalContent)
    {
     …
    
     JsonConverter converter = GetConverter(contract, null, null, null);
    
     if (reader.TokenType == JsonToken.None && !reader.ReadForType(contract, converter != null))
     {
      ......
    
      object deserializedValue;
    
      if (converter != null && converter.CanRead)
      {
       deserializedValue = DeserializeConvertable(converter, reader, objectType, null);
      }
      else
      {
       deserializedValue = CreateValueInternal(reader, objectType, contract, null, null, null, null);
      }
      }
    }
    JSonSerializer Internal Reader 클래스 의 Deserialize 방법 은 현재 요청 한 매개 변수의 유형 에 따라 적합 한 JSonConverter 를 찾 아 예화 하려 고 시도 합 니 다.일치 하 는 컨버터 를 찾 으 면 이 컨버터 를 사용 하여 실제 반 직렬 화 데이터 바 인 딩 작업 을 합 니 다.현재 예 에서 api 의 매개 변수 유형 은 Person 이기 때문에 PersonJSonConverter 와 일치 합 니 다.이것 이 바로 우리 가 PersonJSonConverter 를 추가 함으로써 다 중 데이터 바 인 딩 기능 을 완성 한 이유 입 니 다.
    소스 코드
    총결산
    이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

    좋은 웹페이지 즐겨찾기