У меня проблема с 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();
}