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
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 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.
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!
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" });
});
});
});
}
}
}
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