programing

웹 API에서 속성이 직렬화되는 것을 방지하다

closeapi 2023. 4. 21. 20:53
반응형

웹 API에서 속성이 직렬화되는 것을 방지하다

저는 MVC 4 웹 API와 asp.net 웹 양식 4.0을 사용하여 Rest API를 구축하고 있습니다.정상적으로 동작하고 있습니다.

[HttpGet]
public HttpResponseMessage Me(string hash)
{
    HttpResponseMessage httpResponseMessage;
    List<Something> somethings = ...

    httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK, 
                                 new { result = true, somethings = somethings });

    return httpResponseMessage;
}

이제 몇 가지 속성이 직렬화되는 것을 방지해야 합니다. 수 것을 이지만, 이에서는 LINQ를 사용할 수 있습니다.일반적으로 좋은 접근법이지만 현재 시나리오에서는something오브젝트가 너무 복잡해서 다른 메서드로 다른 속성 세트가 필요하기 때문에 실행 시 무시되는 각 속성을 쉽게 표시할 수 있습니다.

그렇게 할 수 있는 방법이 있나요?

ASP.NET Web API는 기본 포맷으로 사용하기 때문에 응용 프로그램이 데이터 포맷으로만 JSON을 사용하는 경우[JsonIgnore]'CHANGE: 'CHANGE: 'CHANGE: 'CHANGE: 'CHANGE:

public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }

    [JsonIgnore]
    public List<Something> Somethings { get; set; }
}

단, 이 방법은 XML 형식을 지원하지 않습니다.따라서 어플리케이션이 XML 형식을 더 많이 지원해야 하는 경우(또는 XML 형식만 지원해야 합니다.Json.Net 「 」를 합니다.[DataContract]을 모두 합니다.

[DataContract]
public class Foo
{
    [DataMember]
    public int Id { get; set; }
    [DataMember]
    public string Name { get; set; }

    //Ignore by default
    public List<Something> Somethings { get; set; }
}

자세한 내용은 공식 기사를 참조하십시오.

ASP의 Web API 문서 페이지 JSONXML Serialization에 따르면사용할 수 있는 속성의 일련화를 명시적으로 방지하는 NET Web API[JsonIgnore] 또는 Json serializer의 [IgnoreDataMember]「XML」입니다.

하지만 테스트에서 알아차린 것은[IgnoreDataMember]는 XML 요구와 Json 요구 모두에 대해 시리얼화를 방지하므로 여러 속성을 사용하여 속성을 꾸미는 것보다 이 기능을 사용하는 것이 좋습니다.

디폴트로 모든 을 시리얼화하는 것이 아니라, 「opt-in」의 어프로치를 채용할 수 있습니다.이 시나리오에서는 지정한 속성만 직렬화할 수 있습니다. 작업은 시스템에 있는및 을 사용하여 수행합니다.런타임시리얼라이제이션네임스페이스

DataContactAttribute적용되어 , 클래스에는 적용되어 있습니다.DataMemberAttribute이치노

[DataContract]
public class MyClass {

  [DataMember]
  public int Id { get; set;} // Serialized

  [DataMember]
  public string Name { get; set; } // Serialized

  public string DontExposeMe { get; set; } // Will not be serialized
}

내가 감히 이것이 더 나은 접근법이라고 말할 수 있는 이유는 그것이 당신이 연재를 통해 무엇을 할 것인지 아닌지에 대해 명확한 결정을 하도록 강요하기 때문이다.또, 모델 클래스는, JSON.net 에 의존하지 않고, 다른 장소에서 JSON.net 와 연재하고 있는 것만으로 프로젝트내에서 생활할 수 있습니다.

이것은 나에게 효과가 있었습니다: 문자열 배열 유형의 AllowList라는 공용 속성을 가진 커스텀 계약 해결 프로그램을 만듭니다.작업에서 반환해야 하는 작업에 따라 해당 속성을 수정합니다.

1. 커스텀 계약 해결사 작성:

public class PublicDomainJsonContractResolverOptIn : DefaultContractResolver
{
    public string[] AllowList { get; set; }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);

        properties = properties.Where(p => AllowList.Contains(p.PropertyName)).ToList();
        return properties;
    }
}

2. 커스텀 계약 해결사 사용

[HttpGet]
public BinaryImage Single(int key)
{
    //limit properties that are sent on wire for this request specifically
    var contractResolver = Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver as PublicDomainJsonContractResolverOptIn;
    if (contractResolver != null)
        contractResolver.AllowList = new string[] { "Id", "Bytes", "MimeType", "Width", "Height" };

    BinaryImage image = new BinaryImage { Id = 1 };
    //etc. etc.
    return image;
}

이 방법을 사용하면 클래스 정의를 수정하는 대신 특정 요청을 허용/불허용할 수 있습니다., XML 의 없는 는, XML 의 말아 .App_Start\WebApiConfig.cs클라이언트가 json 대신 xml을 요구하면 API가 차단된 속성을 반환합니다.

//remove xml serialization
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);

원하는 것을 실현하기 위한 2가지 방법을 소개합니다.

첫 번째 방법: 필드를 JsonProperty 속성으로 꾸미고 필드가 null일 경우 해당 필드의 시리얼화를 건너뜁니다.

public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }

    [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
    public List<Something> Somethings { get; set; }
}

두 번째 방법:복잡한 시나리오와 네고시에이트 하고 있는 경우는, Web API 표기법( 「Should Serialize」)을 사용하고, 특정의 로직에 의해서 그 필드의 시리얼화를 생략할 수 있습니다.

public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }

    public List<Something> Somethings { get; set; }

    public bool ShouldSerializeSomethings() {
         var resultOfSomeLogic = false;
         return resultOfSomeLogic; 
    }
}

WebApi는 JSON을 사용합니다.Net 및 리플렉션을 사용하여 시리얼라이제이션이 이루어지기 때문에 (예를 들어) WhouseSerialize가 검출되었을 때FieldX() 메서드는 이름이 FieldX인 필드는 시리얼화되지 않습니다.

게임 시작은 늦었지만 익명의 오브젝트가 효과를 발휘합니다.

[HttpGet]
public HttpResponseMessage Me(string hash)
{
    HttpResponseMessage httpResponseMessage;
    List<Something> somethings = ...

    var returnObjects = somethings.Select(x => new {
        Id = x.Id,
        OtherField = x.OtherField
    });

    httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK, 
                                 new { result = true, somethings = returnObjects });

    return httpResponseMessage;
}

도 한번 써보세요.IgnoreDataMember

public class Foo
    {
        [IgnoreDataMember]
        public int Id { get; set; }
        public string Name { get; set; }
    }

[ Ignore Data Member ]를 추가하는 것만으로 정상적으로 동작합니다.

propertyp 위에는 다음과 같은 것이 있습니다.

public class UserSettingsModel
{
    public string UserName { get; set; }
    [IgnoreDataMember]
    public DateTime Created { get; set; }
}

이것은 ApiController에서 동작합니다.코드:

[Route("api/Context/UserSettings")]
    [HttpGet, HttpPost]
    public UserSettingsModel UserSettings()
    {
        return _contextService.GetUserSettings();
    }

greatbear302의 답변과 거의 동일하지만 요청마다 Contract Resolver를 만듭니다.

1) 커스텀 Contract Resolver 작성

public class MyJsonContractResolver : DefaultContractResolver
{
    public List<Tuple<string, string>> ExcludeProperties { get; set; }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (ExcludeProperties?.FirstOrDefault(
            s => s.Item2 == member.Name && s.Item1 == member.DeclaringType.Name) != null)
        {
            property.ShouldSerialize = instance => { return false; };
        }

        return property;
    }
}

2) 커스텀 계약 해결사 사용

public async Task<IActionResult> Sites()
{
    var items = await db.Sites.GetManyAsync();

    return Json(items.ToList(), new JsonSerializerSettings
    {
        ContractResolver = new MyJsonContractResolver()
        {
            ExcludeProperties = new List<Tuple<string, string>>
            {
                Tuple.Create("Site", "Name"),
                Tuple.Create("<TypeName>", "<MemberName>"),
            }
        }
    });
}

편집:

기대했던 대로 작동하지 않았습니다(요청당 Isolate Resolver).익명의 오브젝트를 사용합니다.

public async Task<IActionResult> Sites()
{
    var items = await db.Sites.GetManyAsync();

    return Json(items.Select(s => new
    {
        s.ID,
        s.DisplayName,
        s.Url,
        UrlAlias = s.Url,
        NestedItems = s.NestedItems.Select(ni => new
        {
            ni.Name,
            ni.OrdeIndex,
            ni.Enabled,
        }),
    }));
}

하여 Auto Mapper를 할 수 ..Ignore() 후 합니다.

CreateMap<Foo, Foo>().ForMember(x => x.Bar, opt => opt.Ignore());

.NET Core 3.0 이후의 경우:

ASP(JSON)의 Core is now NET Core( Core now NET Core(NET Core)System.Text.Json의 새로운 )NET Core 3.0 합니다.츠키노텍스트입니다.Json종속성이 하지 않습니다고성능으로 추가 라이브러리 의존이 필요하지 않습니다.

https://learn.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio#newtonsoftjson-jsonnet-support

샘플(고맙습니다)

using System.Text.Json.Serialization;

public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }

    [JsonIgnore]
    public List<Something> Somethings { get; set; }
}

이미 가지고 계신 경우Newtonsoft.Json기본적으로는 설치 후 대신 사용을 선택했습니다.[JsonIgnore]예상대로 되지 않습니다.

웬일인지 그래.[IgnoreDataMember]항상 나에게 효과가 있는 것은 아니다.그리고 나는 가끔 그것을 얻을 수 있다.StackOverflowException(또는 유사).대신 (또는 추가로) 다음과 같은 패턴을 사용하기 시작했습니다.POST에 스며드는.Objects내 API로:

[Route("api/myroute")]
[AcceptVerbs("POST")]
public IHttpActionResult PostMyObject(JObject myObject)
{
    MyObject myObjectConverted = myObject.ToObject<MyObject>();

    //Do some stuff with the object

    return Ok(myObjectConverted);
}

그래서 기본적으로 나는 통과한다.JObject오브젝트를 해석할 때 무한 루프가 발생할 수 있는 내장 시리얼라이저로 인해 발생하는 aviod 문제로 변환합니다.

어떤 식으로든 나쁜 생각인 이유를 아는 사람이 있으면 알려주세요.

문제의 원인이 되는 것은 Entity Framework클래스 속성의 다음 코드입니다(2개의 클래스가 서로 참조하는 경우).

[Serializable]
public partial class MyObject
{
   [IgnoreDataMember]
   public MyOtherObject MyOtherObject => MyOtherObject.GetById(MyOtherObjectId);
}

[Serializable]
public partial class MyOtherObject
{
   [IgnoreDataMember]
   public List<MyObject> MyObjects => MyObject.GetByMyOtherObjectId(Id);
}

언급URL : https://stackoverflow.com/questions/11851207/prevent-property-from-being-serialized-in-web-api

반응형