How to enforce lowercase routes in .NET 6? - .net-6.0

I would like to enforce lowercase routes in .NET 6 API project.
Here is my Program.cs:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Here is my WeatherForecastController.cs:
using Microsoft.AspNetCore.Mvc;
namespace Weather.API.Controllers
{
[ApiController]
[Route("weatherforecast")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
}
I expect only the lowercase route (https://localhost:7243/weatherforecast) to work, but the route w/ pascal/uppercase works as well (https://localhost:7243/Weatherforecast)
I thought I can add builder.Services.AddRouting(options => options.LowercaseUrls = true); and app.UseRouting(), but I'm still able to access the route with pascal/uppercase: https://localhost:7243/Weatherforecast.
Here is the modified Program.cs file that I tried, but doesn't work:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRouting(options => options.LowercaseUrls = true);
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseRouting();
app.MapControllers();
app.Run();

To generate lowercase URLs, add the following to Program.cs.
(Note: this does not enforce lowercase URLs).
// generate lowercase URLs
builder.Services.Configure<RouteOptions>(options =>
{
options.LowercaseUrls = true;
});
var app = builder.Build();
To enforce lowercase URLs, create an IRule-based rule that will redirect any uppercase URLs to lowercase URLs.
see this stackoverflow answer and IRule-based rule documentation.
Once you created an IRule-based rule (i.e., RedirectLowerCaseRule), add the following to Program.cs:
var app = builder.Build();
// enforce lowercase URLs
// by redirecting uppercase urls to lowercase urls
var options = new RewriteOptions().Add(new RedirectLowerCaseRule());
app.UseRewriter(options);

Related

Attempts of migrate from net core 2.2 to 3.0

Good Morning
I'm migrating from 2.2 to 3.0 and then moving to 3.1 in net core with visual studio 2019
my first attempt was the most basic:
docs of Microsoft
Although it is a reduced version, I add it to my code leaving the packages that I still have to use for my project and it does not work ... result: 404
second try:
err_connection_refused
change the ip project of both http and https in addition to comparing it with the .json
result: 404 ERR_CONNECTION_REFUSED
third try:
migrate 2.2 to 3.0 stackoverlofw
reduce it as much as possible but without success too
the fourth attempt was the most "promising"
https://github.com/StackExchangeExamples/DotNetCoreTargetDotNetFramework/tree/master/BasicCoreWebsite
since it showed a "complete" example of how a project with version 3.0 should look
the result: a page that says "hello world" instead of a 404, is not something successful but better than a 404 is not it?
The idea of ​​this post is not only to solve my situation so that I do not get kicked out (lol) but to create as much as possible a complete functional example for future people with the same problem.
Thank you very much in advance and the data you require I will upload with pleasure!
UPDATE (i add the startup.cs)
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using HistoriaClinica.IServicio;
using HistoriaClinica.IServicio.Servicio;
using System;
using System.IO.Compression;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Session;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace HistoriaClinica
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
/// <summary>
/// This method gets called by the runtime. Use this method to add services to the container.
/// </summary>
/// <param name="services"></param>
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
//services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddHttpContextAccessor();
services.AddDistributedMemoryCache();
services.AddSession();
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
services.AddMemoryCache();
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(30);
});
services.AddDataProtection().UseCryptographicAlgorithms(
new AuthenticatedEncryptorConfiguration()
{
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
});
// services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddResponseCompression(options =>
{
options.Providers.Add<GzipCompressionProvider>();
options.EnableForHttps = true;
options.MimeTypes = new[]
{
// General
"text/plain",
// Static files
"text/css",
"application/javascript",
// MVC
"text/html",
"text/xml",
"application/json",
"text/json",
"image/png",
"image/jpeg",
"image/svg+xml",
"application/octet-stream",
"application/font-woff",
"application/pdf"
};
});
services.Configure<GzipCompressionProviderOptions>(options =>
options.Level = CompressionLevel.Optimal
);
services.AddMemoryCache();
// Add application services.
services.AddSingleton<ILoggerServicio, LoggerServicio>();
services.AddScoped<IEncriptaServicio, EncriptaServicio>();
services.AddSingleton<IHttpServicio, HttpServicio>();
services.AddScoped<IPacienteServicio, PacienteServicio>();
services.AddScoped<IProfesionalServicio, ProfesionalServicio>();
services.AddScoped<IEspecialidadServicio, EspecialidadServicio>();
services.AddScoped<ILaboratorioServicio, LaboratorioServicio>();
services.AddScoped<IUsuarioServicio, UsuarioServicio>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<ISessionServicio, SessionServicio>();
services.AddScoped<IImagenServicio, ImagenServicio>();
services.AddScoped<INoticiaServicio, NoticiaServicio>();
services.AddScoped<IResponseServicio, ResponseServicio>();
services.AddScoped<IClienteServicio, ClienteServicio>();
// services.AddMvc(options => options.EnableEndpointRouting = false);
services.AddControllers(options => options.EnableEndpointRouting = false);
services.AddControllersWithViews(options => options.EnableEndpointRouting = false);
services.AddRazorPages().AddMvcOptions(options => options.EnableEndpointRouting = false);
// Cron jobs
//services.AddCronJob<RecordatorioTurno24>(c =>
//{
// c.TimeZoneInfo = TimeZoneInfo.Local;
// c.CronExpression = #"*/1 * * * *";
//});
//services.AddCronJob<RecordatorioTurno48>(c =>
//{
// c.TimeZoneInfo = TimeZoneInfo.Local;
// c.CronExpression = #"* * * * *";
//});
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSession();
app.UseSession();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Cuenta/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "Default",
template: "{controller=Cuenta}/{action=Login}/{id?}");
});
//app.UseEndpoints(endpoints =>
//{
// endpoints.MapControllerRoute(
// name: "default",
// pattern: "{controller=Cuenta}/{action=Login}");
//});
}
}
}

Invalid DTO posted throws exception

Invalid DTO posted to MVC endpoint throws an exception, expecting it to record it in ModelState.
How according to Abp should such exception be handled gracefully?
I see ABP documentation on validating DTOs where it throws the exception for invalid models, but how to pass the error messages to a view.
Using
aspnetboilerplate sample for MVC + .NET Core
Abp package version 4.3
Net Core 2.2
What I did:
Added a DTO
public class ExamDtoBaseTmp
{
[Required(ErrorMessage ="Can't add without a name")]
[StringLength(EntityCommons.MaxExamNameLength,ErrorMessage ="Keep is short")]
public string Name { get; set; }
[Required(ErrorMessage ="Description is needed")]
public string Description { get; set; }
}
Added a POST endpoint that accepted above model
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ExamDtoBaseTmp model)
{
if (!ModelState.IsValid)
{
return View();
}
try
{
// TODO: Add insert logic here
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
Submitted empty form and got below exception:
AbpValidationException: Method arguments are not valid! See ValidationErrors for details.
Abp.Runtime.Validation.Interception.MethodInvocationValidator.ThrowValidationError() in MethodInvocationValidator.cs
Abp.Runtime.Validation.Interception.MethodInvocationValidator.Validate() in MethodInvocationValidator.cs
Abp.AspNetCore.Mvc.Validation.AbpValidationActionFilter.OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) in AbpValidationActionFilter.cs
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync()
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextExceptionFilterAsync()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ExceptionContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
Nivra.Authentication.JwtBearer.JwtTokenMiddleware+<>c__DisplayClass0_0+<<UseJwtTokenMiddleware>b__0>d.MoveNext() in JwtTokenMiddleware.cs
await next();
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) Nivra.Web.Startup.Startup+<>c+<<Configure>b__4_1>d.MoveNext() in Startup.cs
await next();
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context) Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
Startup.cs
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Castle.Facilities.Logging;
using Abp.AspNetCore;
using Abp.Castle.Logging.Log4Net;
using Nivra.Authentication.JwtBearer;
using Nivra.Configuration;
using Nivra.Identity;
using Nivra.Web.Resources;
using Abp.AspNetCore.SignalR.Hubs;
using System.IO;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using System.Linq;
using Abp.Extensions;
using Swashbuckle.AspNetCore.Swagger;
using Microsoft.AspNetCore.Mvc.Cors.Internal;
using Nivra.OnlineExam.Models;
using Nivra.OnlineExam.Service;
using Microsoft.Extensions.Options;
namespace Nivra.Web.Startup
{
public class Startup
{
private const string _defaultCorsPolicyName = "localhost";
private readonly IConfigurationRoot _appConfiguration;
public Startup(IHostingEnvironment env)
{
_appConfiguration = env.GetAppConfiguration();
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.Configure<ExamDatabaseSettings>(
_appConfiguration.GetSection(nameof(ExamDatabaseSettings))
);
services.AddSingleton<IExamDatabaseSettings>(sp =>
sp.GetRequiredService<IOptions<ExamDatabaseSettings>>().Value);
services.AddSingleton<ExamService>();
// MVC
services.AddMvc(
options =>
options.Filters.Add(new CorsAuthorizationFilterFactory(_defaultCorsPolicyName))
//options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute())
);
services.AddSpaStaticFiles(c =>
{
c.RootPath = "wwwroot/dist";
});
IdentityRegistrar.Register(services);
AuthConfigurer.Configure(services, _appConfiguration);
services.AddSignalR();
// Configure CORS for angular2 UI
services.AddCors(
options => options.AddPolicy(
_defaultCorsPolicyName,
builder => builder
.AllowAnyOrigin()
//.WithOrigins(
// // App:CorsOrigins in appsettings.json can contain more than one address separated by comma.
// _appConfiguration["App:CorsOrigins"]
// .Split(",", StringSplitOptions.RemoveEmptyEntries)
// .Select(o => o.RemovePostFix("/"))
// .ToArray()
//)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
)
);
// Swagger - Enable this line and the related lines in Configure method to enable swagger UI
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Info { Title = "aspnetCoreNg API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
// Define the BearerAuth scheme that's in use
options.AddSecurityDefinition("bearerAuth", new ApiKeyScheme()
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
Name = "Authorization",
In = "header",
Type = "apiKey"
});
});
services.AddScoped<IWebResourceManager, WebResourceManager>();
var configurations = AppConfigurations.Get(AppDomain.CurrentDomain.BaseDirectory);
services.Configure<ExamDatabaseSettings>(configurations.GetSection(nameof(ExamDatabaseSettings)));
// Configure Abp and Dependency Injection
return services.AddAbp<NivraWebMvcModule>(
// Configure Log4Net logging
options => options.IocManager.IocContainer.AddFacility<LoggingFacility>(
f => f.UseAbpLog4Net().WithConfig("log4net.config")
)
);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAbp(options => { options.UseAbpRequestLocalization = false; }); // Initializes ABP framework.
app.UseCors(_defaultCorsPolicyName); // Enable CORS!
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.Use(async (context, next) =>
{
await next();
if (
context.Response.StatusCode == 404
&& !Path.HasExtension(context.Request.Path.Value)
&& !context.Request.Path.Value.StartsWith("/api/services", StringComparison.InvariantCultureIgnoreCase)
)
{ context.Request.Path = "/index.html"; await next(); }
});
app.UseAuthentication();
app.UseJwtTokenMiddleware();
app.UseSignalR(routes =>
{
routes.MapHub<AbpCommonHub>("/signalr");
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "defaultWithArea",
template: "{area}/{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
// app.Map("/spa", l =>
// l.UseSpa(spa =>
// {
// // To learn more about options for serving an Angular SPA from ASP.NET Core,
// // see https://go.microsoft.com/fwlink/?linkid=864501
// spa.Options.SourcePath = "./";
// if (env.IsDevelopment())
// {
// spa.UseAngularCliServer(npmScript: "start");
// }
// })
//);
app.UseSwagger();
//Enable middleware to serve swagger - ui assets(HTML, JS, CSS etc.)
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "AbpZeroTemplate API V1");
}); //URL: /swagger
}
}
}
Abp wraps only exception thrown in actions with ObjectResult return type by default. e.g. public JsonResult Create();
If your action is return a view result, it is recommended to catch the exception within the action yourself and add to ModelState manually.
see https://aspnetboilerplate.com/Pages/Documents/AspNet-Core#exception-filter
Referring above link
Exception handling and logging behaviour can be changed using the
WrapResult and DontWrapResult attributes for methods and classes.

No service for type Identity.RoleManager - Identity.IdentityRole has been registered error

I use Microsoft Identity for the first time. I configured users and roles with IdentityUser and IdentityRole. I want to assign a role to users in Startup.cs. I wrote a method to make it which is
private async Task CreateUserRoles(IServiceProvider serviceProvider) {
var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole<int>>>();
var userManager = serviceProvider.GetRequiredService<UserManager<User>>();
var roleName = "Super Admin";
var roleCheck = await roleManager.RoleExistsAsync(roleName);
if (!roleCheck) {
Role role = new Role();
role.Name = roleName;
IdentityResult result = roleManager.CreateAsync(role).Result;
//IdentityResult roleResult = await roleManager.CreateAsync(new IdentityRole<int>(roleName));
}
User user = new User();
user.UserName = "someone";
user.Password = "someone";
user.Email = "someone#gmail.com";
ApplicationDbContext context = new ApplicationDbContext();
context.Users.Add(user);
context.SaveChanges();
user = await userManager.FindByEmailAsync("someone#gmail.com");
await userManager.AddToRoleAsync(user, roleName);
}
Hovewer there is a problem :
No service for type
'Microsoft.AspNetCore.Identity.RoleManager1[Microsoft.AspNetCore.Identity.IdentityRole1[System.Int32]]' has been registered.
How can I fix it?
Here is a Startup.cs
public class Startup {
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) {
services.Configure<CookiePolicyOptions>(options => {
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// Add MVC services to the services container.
services.AddMvc();
services.AddDistributedMemoryCache(); // Adds a default in-memory implementation of IDistributedCache
services.AddSession(opt => { opt.Cookie.IsEssential = true; });
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddRazorPagesOptions(options => {
options.AllowAreas = true;
options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Settings");
options.Conventions.AuthorizeAreaPage("Identity", "/Account/Logout");
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql("User ID = postgres; Password = sa; Host = localhost; Port = 5432; Database = CCN; Pooling = true;"));
services.ConfigureApplicationCookie(options => {
options.LoginPath = $"/Account/Login"; //options.LoginPath = $"/Identity/Account/Login";
options.LogoutPath = $"/Account/Logout";
options.AccessDeniedPath = $"/Account/AccessDenied";
});
//Password settings
services.AddIdentity<User, Role>(o => {
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false;
//o.Password.RequiredLength = 3;
}).AddEntityFrameworkStores<ApplicationDbContext>().AddRoles<IdentityRole>()
.AddDefaultTokenProviders();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider services) {
app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
else {
app.UseExceptionHandler("/Home/Index");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
//Enable session
app.UseSession();
app.UseAuthentication();
app.UseMvc(routes => {
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}");
});
//Create user role and assign it
CreateUserRoles(services).Wait();
}
}
No service for type
'Microsoft.AspNetCore.Identity.RoleManager1[Microsoft.AspNetCore.Identity.IdentityRole1[System.Int32]]' has been registered.
When registering IdentityRole for the AspNetCore Identity, the RoleManager<> type will be registered to the ServiceCollection as RoleManager<IdentityRole>.
Whenever you want to resolve the RoleManager<>, specify the identity role model registered in your startup as the type parameter. Which would be RoleManager<IdentityRole> in your specific case.
When calling GetRequiredService<RoleManager<IdentityRole>>() on the resulting service provider, GetRequiredService<RoleManager<IdentityRole>>() will throw the above exception.
Make the below modification:
In CreateUserRoles
var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
Register the role services in the DI container (choose one of the two methods)
1.Use AddIdentity()
services.AddIdentity<User, IdentityRole()
.AddDefaultUI()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
2.Use AddDefaultIdentity , include roles by using the [AddRoles][1] method
services.AddDefaultIdentity<User>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Reference : AddDefaultIdentity and AddIdentity
I tried those solutions but it didn't work. Then I recognize that I created an entity called Role which is derived from IdentityRole and its id data type is int. I changed
var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole<int>>>();
this line with
var roleManager = serviceProvider.GetRequiredService<RoleManager<Role>>();
this one. Then it works.
On the other hand, thanks for your help!

Inconsistent culture - decimal separator ignored in model binding between razor view and viewmodel

I have the following behaviour in my program:
User input for a decimal variable
A) jquery validation turned off:
1) If the user uses a comma as decimal separator, the value is stored correctly in the ViewModel
2) If the user uses a point as decimal separator, the value is multiplied by 100 (as if there was no decimal separator)
B) jquery validation turned on:
1) I get an error, that a number must be supplied
2) same Problem as A2)
However if I display a decimal value of the ViewModel in the view it is shown per default with a point as a decimal separator.
This inconsistency is confusing me and I would like to implement a consistent behaviour, but unfortunately I don't know what I am actually looking for.
The website will be localized in german and italian. The localization works without any problems, so far.
This is my
startup.cs
namespace XXX
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Added - uses IOptions<T> for your settings.
services.AddOptions();
// Added - Confirms that we have a home for our DemoSettings
services.Configure<DatabaseSettings>(Configuration.GetSection("DatabaseSettings"));
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseStatusCodePagesWithReExecute("/Error/Index", "?i_statusCode={0}");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
IList<CultureInfo> supportedCultures = new List<CultureInfo>
{
new CultureInfo("de"),
new CultureInfo("it"),
};
var localizationOptions = new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("de"),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
};
var requestProvider = new RouteDataRequestCultureProvider();
localizationOptions.RequestCultureProviders.Insert(0, requestProvider);
app.UseRouter(routes =>
{
routes.MapMiddlewareRoute("{culture=de}/{*mvcRoute}", subApp =>
{
subApp.UseRequestLocalization(localizationOptions);
subApp.UseMvc(mvcRoutes =>
{
mvcRoutes.MapRoute(
name: "defaultLocalized",
template: "{culture=de}/{controller=Contract}/{action=Index}/{id?}");
mvcRoutes.MapRoute(
name: "error",
template: "Error/Index",
defaults: new { controller = "Error", action = "Index", culture = "de" });
mvcRoutes.MapRoute(
name: "default",
template: "{*catchall}",
defaults: new { controller = "Home", action = "Index", culture = "de" });
});
});
});
}
}
}

OData Action Parameters is null in run time

I was trying to define a Post OData Web API action.The parameter is a number.
I found an article
And I followed as the article described, here is the source code:
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.MapODataServiceRoute("odata", "odata", model: GetModel());
}
private static Microsoft.OData.Edm.IEdmModel GetModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<ThumbnailQueue>("ThumbnailQueues");
builder.EntitySet<Document>("Documents");
EntitySetConfiguration<ThumbnailQueue> thumbnailQueue = builder.EntitySet<ThumbnailQueue>("ThumbnailQueues");
var action = thumbnailQueue.EntityType.Action("PostThumbnailQueue");
action.Parameter<long>("DocumentSN");
var postThumbnailQueue = thumbnailQueue.EntityType.Collection.Action("PostThumbnailQueue");
postThumbnailQueue.Parameter<long>("DocumentSN");
return builder.GetEdmModel();
}
}
Web API
public class ThumbnailQueuesController : ODataController
{
private MarketingEntities db = new MarketingEntities();
// POST: odata/ThumbnailQueues
[HttpPost]
public IHttpActionResult PostThumbnailQueue(ODataActionParameters parameters)
{
if (parameters == null) return BadRequest();
var documentSN = (long)parameters["DocumentSN"];
if (db.ThumbnailQueues.Any(t => t.DocumentSN == documentSN))
{
return BadRequest("Record already exists");
}
var myThumbnailQueue = new ThumbnailQueue();
myThumbnailQueue.DocumentSN = documentSN;
myThumbnailQueue.Status = 1;
db.ThumbnailQueues.Add(myThumbnailQueue);
db.SaveChanges();
return Ok();
}
I used Postman to test the function
But in the run time, the parameter is null, so I can not get documentSN from the parameter. I have troubleshoot this porblem for hours, could anybody help me to fix this problem? Many thanks~
You need to use the following route for that: http://localhost:10076/odata/ThumbnailQueues/Default.PostThumbnailQueue
ThumbnailQueues - entity set
Default - default namespace
PostThumbnailQueue - action

Resources