【问题标题】:EFCore/ASP.NET Core Many-to-many relation: JsonSerializer ReferenceLoopHandling.Ignore not working as expectedEFCore/ASP.NET Core 多对多关系:JsonSerializer ReferenceLoopHandling.Ignore 未按预期工作
【发布时间】:2019-08-03 10:05:19
【问题描述】:

我对 ASP.NET Core JsonSerializer 有疑问。即使我将 ReferenceLoopHandling 属性显式设置为 ReferenceLoopHandling.Ignore,它仍然会进入无限引用循环。

我在这个 github 问题中有详细信息:https://github.com/aspnet/EntityFrameworkCore/issues/14997

编辑:

整个启动配置代码:

public void ConfigureServices(IServiceCollection services)
{
  // Deserialize specific settings into living objects
  services.Configure<JwtSettings>(this.Configuration.GetSection("Jwt"));
  services.Configure<CookieSettings>(this.Configuration.GetSection("Cookies"));

  // Read settings to apply locally
  DatabasesSettings dbSettings = DatabasesSettings.GetDatabasesSettings(this.Configuration);
  JwtSettings jwtSettings = this.Configuration.GetSection("Jwt").Get<JwtSettings>();
  CookieSettings cookieSettings = this.Configuration.GetSection("Cookies").Get<CookieSettings>();

  switch (dbSettings[DatabaseConstants.DatabaseSettingsData].Provider)
  {
    case "mysql":
      services.AddEntityFrameworkMySql()
              .AddDbContext<DataContext>(options => options.UseMySql(ConnectionStringBuilder.BuildConnectionString(dbSettings[DatabaseConstants.DatabaseSettingsData])));
      break;
    case "mssql":
      services.AddEntityFrameworkSqlServer()
              .AddDbContext<DataContext>(options => options.UseSqlServer(ConnectionStringBuilder.BuildConnectionString(dbSettings[DatabaseConstants.DatabaseSettingsData])));
      break;
  }

  // Identity MUST precede AUTHENTICATION in order to produce correct 401 Unauthorized instead of 404 Not found
  services.AddIdentity<User, IdentityRole>(options =>
  {
    options.User.RequireUniqueEmail = true;
    options.Password.RequiredLength = 6;
    options.Password.RequireDigit = false;
    options.Password.RequireNonAlphanumeric = false;
    options.Password.RequireUppercase = false;
    options.Password.RequireLowercase = false;
  }).AddEntityFrameworkStores<DataContext>().AddDefaultTokenProviders();

  // JWT Bearer Token Authentication configuration secretKey contains a secret passphrase only your server knows
  Action<JwtBearerOptions> jwtOptions = options =>
  {
    options.Events = new JwtBearerEvents
    {
      OnTokenValidated = context =>
      {
        // Get the EF Context responsbile for authentication
        DataContext dbContext = context.HttpContext.RequestServices.GetRequiredService<DataContext>();

        List<Claim> claims = new List<Claim>
        {
          new Claim("AuthorizedAccess", "true"),
        };

        // Add claim(s) based on user -- todo
        ClaimsIdentity appIdentity = new ClaimsIdentity(claims);
        context.Principal.AddIdentity(appIdentity);

        return Task.CompletedTask;
      }
    };
    options.TokenValidationParameters = new TokenValidationParameters
    {
      // Do not validate lifetime. Need logout to invalidate. todo: verify if we can leave it this way
      ValidateLifetime = jwtSettings.Expiration.Enabled,

      // If you want to allow a certain amount of clock drift, set that here:
      ClockSkew = TimeSpan.FromMinutes(jwtSettings.Expiration.ClockDrift),

      // Validate the JWT Issuer (iss) claim
      ValidateIssuer = true,
      ValidIssuer = jwtSettings.Issuer,

      // Validate the JWT Audience (aud) claim
      ValidateAudience = true,
      ValidAudience = jwtSettings.Audience,

      // The signing key must match!
      ValidateIssuerSigningKey = true,
      IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.Key)),

      SaveSigninToken = true
    };
  };
  // Authentication Service
  services.AddAuthentication(
    options =>
    {
      options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
      options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddJwtBearer(jwtOptions).AddCookie();

  // Authorization Service
  services.AddAuthorization(
    options =>
    {
      options.AddPolicy(PolicyNames.AdministrationPolicy, policy => policy.Requirements.Add(new AuthorizationNameRequirement(PolicyNames.AdministrationPolicy)));
      options.AddPolicy(PolicyNames.PurchasePolicy, policy => policy.Requirements.Add(new AuthorizationNameRequirement(PolicyNames.PurchasePolicy)));
      options.AddPolicy(PolicyNames.TradePolicy, policy => policy.Requirements.Add(new AuthorizationNameRequirement(PolicyNames.TradePolicy)));
      options.AddPolicy(PolicyNames.ExchangePolicy, policy => policy.Requirements.Add(new AuthorizationNameRequirement(PolicyNames.ExchangePolicy)));
    });

  // Application Cookie
  services.ConfigureApplicationCookie(
    options =>
    {
      // Avoid redirecting REST clients on 401
      options.Events = new CookieAuthenticationEvents
      {
        OnRedirectToLogin = ctx =>
        {
          ctx.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
          return Task.FromResult(0);
        }
      };

      options.Cookie.Name = cookieSettings.ApplicationCookieName;
    });

  // Antiforgery
  services.AddAntiforgery(
    options =>
    {
      options.Cookie.Name = cookieSettings.AntiforgeryCookieName;
      options.Cookie.Domain = cookieSettings.AntiforgeryCookieDomain;
      options.Cookie.Path = "/";
      options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    });

  // Additional Services
  services.AddSingleton<IAuthorizationHandler, AuthorizationNameHandler>();

  // Cross Origin Policies
  services.AddCors(
    options =>
    {
      options.AddPolicy("AllowAnyOrigin", builder => { builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowCredentials().Build(); });
    });

  // Enable Response Compression
  services.AddResponseCompression();

  // MVC
  services.AddMvc(
    options =>
    {
      options.Filters.Add(new CorsAuthorizationFilterFactory("AllowAnyOrigin"));
      options.CacheProfiles.Add(
        "Default",
        new CacheProfile()
        {
          Duration = 60
        });
      options.CacheProfiles.Add(
        "Never",
        new CacheProfile()
        {
          Location = ResponseCacheLocation.None,
          NoStore = true
        });
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2).AddJsonOptions(
    options =>
    {
      options.SerializerSettings.Formatting = Formatting.Indented;
      options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
      options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
    });

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
  if (Program.IsDebug)
  {
    app.UseDeveloperExceptionPage();
  }
  else
  {
    app.UseHsts();
  }

  app.UseCors("AllowAnyOrigin");

  // app.UseCorsMiddleware(); // Custom middleware
  app.UseHttpsRedirection();

  app.UseDefaultFiles();
  app.UseStaticFiles();
  app.UseAuthentication();
  app.UseResponseCompression();

  app.UseMvc();
}

【问题讨论】:

  • 显示你尝试过的启动配置代码。
  • @TanvirArjel 给你

标签: asp.net-core json.net ef-core-2.2


【解决方案1】:

您可以将Json.NET 配置为忽略它在对象图中找到的循环。这是在Startup.cs 中的ConfigureServices(...) 方法中完成的,如下所示:

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddMvc()
        .AddJsonOptions(
            options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
        );

    ...
}

在您的代码中,将 options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Error; 替换为以下内容:

options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore

【讨论】:

  • 对不起。我把我测试过的代码放在那里,忘了改回来。我实际上在忽略它。还是不行。
  • 在 github 问题中您可以看到,我使用 Ignore 作为 JSON 设置。我只是为这个问题输入了错误的代码。现在改了。
  • 必须再次尝试ignore 吗?不管用?如果不是,那么它显示的错误消息是什么?
  • 是的,我已经尝试过忽略。不,它不起作用,并且没有特定的错误消息。序列化程序只是在无限循环中不断解析自引用。当我将设置设置为错误时,序列化程序甚至会警告我关于自引用,这是完全正确的。但是,如果我告诉它,它为什么不忽略它们呢??
猜你喜欢
  • 1970-01-01
  • 2018-12-28
  • 1970-01-01
  • 2021-11-27
  • 2021-10-01
  • 2015-10-26
  • 2018-06-05
  • 2022-07-08
  • 1970-01-01
相关资源
最近更新 更多