如何在ASP.NET Core中的请求之间获得独立的JSON参考解析?

时间:2019-03-21 00:12:50

标签: asp.net-core json.net

我试图将自定义IReferenceResolver实现添加到ASP.NET Core 2.2 MVC API应用程序,以减少JSON有效负载中的数据。但是,参考分辨率在不同的请求之间共享。

似乎在请求之间共享ReferenceResolver的单个实例。我希望独立于其他请求来解析引用,因为我的不同用户将没有此共享引用上下文。

这是我在 Startup.cs 中的process_pid=$(ps --no-headers aux | grep "${process_cmd_line}" | grep -v grep | awk '{print $2}' | tr '\n' '') 方法:

ConfigureServices

这是我的控制器实现以及自定义的 IReferenceResolver

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc()
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
                .AddJsonOptions(opts =>
                {
                    opts.SerializerSettings.ReferenceResolverProvider = () => new ThingReferenceResolver();
                });
        }

我的第一个请求得到以下答复:

    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        [HttpGet("")]
        public ActionResult<ThingsResponse> Get()
        {
            return new ThingsResponse
            {
                MainThing = new Thing { Id = "foo" },

                Things = new List<Thing>
                {
                    new Thing { Id = "foo" },
                    new Thing { Id = "bar" }
                }
            };
        }
    }

    public class ThingsResponse
    {
        [JsonProperty(IsReference = true)]
        public Thing MainThing { get; set; }

        [JsonProperty(ItemIsReference = true)]
        public List<Thing> Things { get; set; }
    }

    public class Thing
    {
        public string Id { get; set; }
    }

    public class ThingReferenceResolver : IReferenceResolver
    {
        private readonly IDictionary<string, Thing> _idReference = new Dictionary<string, Thing>();

        public void AddReference(object context, string reference, object value)
        {
            _idReference[reference] = (Thing)value;
        }

        public string GetReference(object context, object value)
        {
            var thing = (Thing)value;

            _idReference[thing.Id] = thing;

            return thing.Id.ToString();
        }

        public bool IsReferenced(object context, object value)
        {
            var thing = (Thing)value;

            return _idReference.ContainsKey(thing.Id);
        }

        public object ResolveReference(object context, string reference)
        {
            _idReference.TryGetValue(reference, out Thing thing);

            return thing;
        }
    }

在我的第二个请求上,我收到以下响应:

{
  "mainThing": {
    "$id": "foo",
    "id": "foo"
  },
  "things": [
    {
      "$ref": "foo"
    },
    {
      "$id": "bar",
      "id": "bar"
    }
  ]
}

我希望我的第二个请求看起来像我的第一个请求,即可重复的输出。

2 个答案:

答案 0 :(得分:1)

对于第二个请求,您将获得不同的结果,因为MVC创建了一个序列化器并对其进行缓存,如果您像这样对引用进行跟踪,则该序列化器将对引用进行缓存。

我认为,如果您在每个结果中返回带有新序列化程序设置的JsonResult,那么您将不会遇到此问题:

new JsonResult(yourData, new JsonSerializerSettings { ... })

答案 1 :(得分:0)

我想出的一个选择是绕过配置MVC提供的JSON序列化程序,并为有问题的请求创建自己的序列化程序。

        [HttpGet("")]
        public ActionResult<ThingsResponse> Get()
        {
            var serializerSettings = JsonSerializerSettingsProvider.CreateSerializerSettings();

            serializerSettings.ReferenceResolverProvider = () => new ThingReferenceResolver();

            return new JsonResult(
                new ThingsResponse
                {
                    MainThing = new Thing { Id = "foo" },

                    Things = new List<Thing>
                    {
                        new Thing { Id = "foo" },
                        new Thing { Id = "bar" }
                    }
                },

                serializerSettings
            );
        }

在我的特定情况下,这是可以的,因为我没有很多端点需要配置。

这意味着不需要示例中的以下代码来解决我的问题(因为我是根据每个请求定义的)

                .AddJsonOptions(opts =>
                {
                    opts.SerializerSettings.ReferenceResolverProvider = () => new ThingReferenceResolver();
                });

我认为我会根据自己的情况选择此选项,但很想知道是否有更好的方法来实现它。