在每个请求问题中创建JSON ContractResolver

时间:2018-10-19 19:58:51

标签: json asp.net-core

我有一个.NET Core Web Api 2.1应用程序,在该应用程序中,我仅序列化了客户端请求的属性。

示例:GET orders/1?select=Id,TotalAmount

示例:GET orders/1?select=Id,CustomerName,DeliveryAddress,Location,ZipCode

为此,该应用会在每个请求(生命周期范围)中创建一个对象,以获取请求的属性。

然后,我创建了自定义ContractResolver类,该类在每个请求中均由全局IResultFilter实例化:

public class SerializationFilter : IResultFilter
{
    private readonly IApiQueryParameters _apiQueryParameters;

    public SerializationFilter(IApiQueryParameters apiQueryParameters)
    {
        this._apiQueryParameters = apiQueryParameters;
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        var objectResult = context.Result as ObjectResult;

        if (objectResult != null)
        {
            var contractResolver = new SelectiveResourceContractResolver(this._apiQueryParameters);
            var serializerSettings = new JsonSerializerSettings
            {
                ContractResolver = contractResolver
            };

            var jsonFormatter = new JsonOutputFormatter(
                serializerSettings,
                ArrayPool<char>.Shared);

            objectResult.Formatters.Add(jsonFormatter);
        }
    }
}

现在,这适用于应用程序在线后发出的第一个请求。 第二个请求正确创建了ContractResolver,但是响应不正确。返回的序列化属性不是客户端请求的属性。

调试代码,我注意到在创建SelectiveResourceContractResolver时,它会正确访问构造函数方法

    public SelectiveResourceContractResolver(IApiQueryParameters apiQueryParameters)
    {
        this._apiQueryParameters = apiQueryParameters;            
    }

此外,它运行CreateProperty重写方法:

 protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)

但是在第二个请求中,CreatePropertyMethod没有运行。

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

        property.ShouldSerialize =
            instance =>
            {
                ...
            };

        return property;
    }

第二次,它只是执行property.ShouldSerialize = instance =>部分。我认为应用程序会进行某种形式的缓存,但是更奇怪的是,执行过滤器时,每个请求都创建了ContractResolver(未在Startup.cs中全局设置)。另外,当我在第二个请求中调试它时,ContractResolver中的IApiQueryParameters属性具有第一个请求的所有值,而不是构造函数的第二次执行的所有值。

我在这里想念什么?

1 个答案:

答案 0 :(得分:0)

我假设您的SelectiveResourceContractResolver继承自DefaultContractResolver

DefaultContractResolver.ResolveContract,它在序列化期间在内部被here调用,因此CreateContract(依次调用CreateProperty)对于每种类型仅被调用一次,直到您的WebAPI进程运行。

这是因为调用CreateContract很昂贵,并且不缓存合同会严重影响序列化性能。

如果您只需要缓存每个响应的合同,则可以在SelectiveResourceContractResolver中创建自己的实例缓存,并覆盖ResolveContract来使用缓存:

public class InstanceCachingContractResolver : DefaultContractResolver
{
    private readonly Dictionary<Type, JsonContract> contractCache = new Dictionary<Type, JsonContract>();

    public override JsonContract ResolveContract(Type type)
    {
        if (!contractCache.TryGetValue(type, out JsonContract contract))
        {
            contract = CreateContract(type);
            contractCache.Add(type, contract);
        }
        return contract;
    }
}