C# ASP.NET Core 3.0 Web API路由不工作
我正在尝试访问dotnet core 3.1解决方案中的jwt控制器 这是我的jwt控制器:C# ASP.NET Core 3.0 Web API路由不工作,c#,asp.net-core,.net-core,C#,Asp.net Core,.net Core,我正在尝试访问dotnet core 3.1解决方案中的jwt控制器 这是我的jwt控制器: [Authorize] [ApiController] [Route("api/[controller]")] public class JwtController : ControllerBase { #region Variables private readonly IUserAuthorisationServices _tokenService;
[Authorize]
[ApiController]
[Route("api/[controller]")]
public class JwtController : ControllerBase
{
#region Variables
private readonly IUserAuthorisationServices _tokenService;
private readonly IOptions<JwtTokenOptions> jwtOptions;
private readonly ILogger logger;
private readonly JsonSerializerSettings _serializerSettings;
#endregion
public JwtController(IUserAuthorisationServices tokenService,
IOptions<JwtTokenOptions> jwtOptions,
ILoggerFactory loggerFactory)
{
if (loggerFactory is null) throw new ArgumentNullException(nameof(loggerFactory));
_tokenService = tokenService ?? throw new ArgumentNullException(nameof(tokenService));
jwtOptions = jwtOptions ?? throw new ArgumentNullException(nameof(jwtOptions));
logger = loggerFactory.CreateLogger<JwtController>();
_serializerSettings = new JsonSerializerSettings {
Formatting = Formatting.Indented
};
//loggingRepository = _errorRepository;
//ThrowIfInvalidOptions(this.jwtOptions);
}
[AllowAnonymous]
[Route("authenticate")]
[HttpPost]
public async Task<IActionResult> Authenticate([FromBody] LoginViewModel model)
{
var userContext = _tokenService.Authenticate(model.Email, model.Password);
if (userContext.Principal == null) {
logger.LogInformation($"Invalid username ({model.Email}) or password ({model.Password})");
return BadRequest(new { message = "Username or password is incorrect" });
}
return Ok(await _tokenService.CreateTokenAsync(userContext).ConfigureAwait(false));
}
[Route("JWTStatus")]
[HttpGet]
public static IActionResult GetJwtStatus()
{
// It made it here so it was authenticated.
return new OkResult();
}
}
我注意到fetch使用的是“api/authenticate”,当我查看HttpRequest时,它得到了相同的url,但它找不到它
这是它在返回之前访问的中间件
namespace JobsLedger.AUTHORISATION.API.SessionMiddleware
{
public class ConfigureSessionMiddleware
{
private readonly RequestDelegate _next;
public ConfigureSessionMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext, IUserSession userSession, ISessionServices sessionServices)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
if (userSession == null)
{
throw new ArgumentNullException(nameof(userSession));
}
if (sessionServices == null)
{
throw new ArgumentNullException(nameof(sessionServices));
}
if (httpContext.User.Identities.Any(id => id.IsAuthenticated))
{
if (httpContext.Session.GetString("connectionString") == null) // Session needs to be set..
{
userSession.UserId = httpContext.User.Claims.FirstOrDefault(x => x.Type == "userId")?.Value;
userSession.ConnectionString = sessionServices.ConnectionStringFromUserId(userSession.UserId);
httpContext.Session.SetString("userId", userSession.UserId);
httpContext.Session.SetString("connectionString", userSession.ConnectionString);
}
else // Session set so all we need to is to build userSession for data access..
{
userSession.UserId = httpContext.Session.GetString("userId");
userSession.ConnectionString = httpContext.Session.GetString("connectionString");
}
}
// Call the next delegate/middleware in the pipeline
await _next.Invoke(httpContext).ConfigureAwait(false);
}
}
}
由于还没有设置用户身份,因此它应该直接跳过此设置。因此,它实际上直接指向:
await _next.Invoke(httpContext).ConfigureAwait(false);
然后转到控制器操作。。但它只是直接用404
在输出窗口中,我得到:
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/2.0 POST https://localhost:44301/api/authenticate text/plain;charset=UTF-8 76
Microsoft.AspNetCore.Cors.Infrastructure.CorsService: Information: CORS policy execution successful.
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 17.1363ms 404
你可以看到404
怎么找不到动作
这可能是因为它是https和HTTP2.0吗
为了完整起见,这里是该中间件的httpContext:
更新…
我不使用MVC在启动或只是端点
可能是Startup.cs中的管道。。这是我的startup.cs
[assembly: NeutralResourcesLanguage("en")]
namespace JobsLedger.API {
public class Startup {
//private const string SecretKey = "needtogetthisfromenvironment";
//private readonly SymmetricSecurityKey
// _signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey));
private readonly IWebHostEnvironment _env;
private readonly IConfiguration _configuration; // { get; }
public Startup(IWebHostEnvironment env, IConfiguration configuration) {
_env = env;
_configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) {
// Add framework services.
services.AddCors();
services.AddControllers();
services.AddOptions();
services.AddDbContext<CATALOGContext>(options => options.UseSqlServer(_configuration.GetConnectionString("CatalogConnection"), b => b.MigrationsAssembly("JobsLedger.CATALOG")));
services.AddDbContext<DATAContext>(options => options.UseSqlServer(_configuration.GetConnectionString("TenantDbConnection"), a => a.MigrationsAssembly("JobsLedger.DATA")));
// Make authentication compulsory across the board (i.e. shut
// down EVERYTHING unless explicitly opened up).
// Use policy auth.
services.AddAuthorization(options => {
options.AddPolicy("TenantAdmin", policy => policy.RequireClaim(ClaimTypes.Role, "TenantAdmin"));
options.AddPolicy("Admin", policy => policy.RequireClaim(ClaimTypes.Role, "Admin"));
options.AddPolicy("Employee", policy => policy.RequireClaim(ClaimTypes.Role, "Employee"));
});
//services.ConfigureApplicationInjection();
// Get jwt options from app settings
var tokenOptions = _configuration.GetSection("Authentication").Get<JwtTokenOptions>();
services.AddAuthentication(x => {
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
RequireExpirationTime = true,
ValidIssuer = tokenOptions.Issuer,
ValidAudience = tokenOptions.Audience,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(tokenOptions.SigningKey)),
ClockSkew = TimeSpan.Zero
};
});
services
.AddDistributedMemoryCache()
.AddSession()
// Repositories - DATA
.AddScoped<IClientDATARepository, ClientDATARepository>()
.AddScoped<ILoggingDATARepository, LoggingDATARepository>()
.AddScoped<IJobDATARepository, JobDATARepository>()
.AddScoped<IBrandDATARepository, BrandDATARepository>()
.AddScoped<ITypeDATARepository, TypeDATARepository>()
.AddScoped<IStateDATARepository, StateDATARepository>()
.AddScoped<IStatusDATARepository, StatusDATARepository>()
.AddScoped<ISuburbDATARepository, SuburbDATARepository>()
.AddScoped<ICounterDATARepository, CounterDATARepository>()
// Repositories - CATALOG
.AddScoped<ITenantCATALOGRepository, TenantCATALOGRepository>()
.AddScoped<IUserCATALOGRepository, UserCATALOGRepository>()
.AddScoped<IRoleCATALOGRepository, RoleCATALOGRepository>()
.AddScoped<ICounterCATALOGRepository, CounterCATALOGRepository>()
.AddScoped<ISuburbCATALOGRepository, SuburbCATALOGRepository>()
.AddScoped<IStateCATALOGRepository, StateCATALOGRepository>()
// Business services
// Services - API
.AddScoped<IClientServices, ClientServices>()
.AddScoped<IJobServices, JobServices>()
//Services - Catalog
.AddScoped<ITenantServices, TenantServices>()
.AddScoped<IUserServices, UserServices>()
//.AddScoped<IUserValidateService, UserValidateService>()
// Services - Shared
.AddScoped<IAddressDropdownServices, AddressDropdownServices>()
//Services - Auth
.AddScoped<ICryptoService, CryptoService>()
.AddScoped<IUserAuthorisationServices, UserAuthorisationServices>()
.AddScoped<IUserSession, UserSession>()
.AddScoped<ISessionServices, SessionServices>()
// CATALOG services - Initialisations.
.AddScoped<ICATALOGCounterInitialiser, CATALOGCounterInitialiser>()
.AddScoped<ICATALOGStateInitialiser, CATALOGStateInitialiser>()
.AddScoped<ICATALOGSuburbInitialiser, CATALOGSuburbInitialiser>()
.AddScoped<IRoleInitialiser, CATALOGRoleInitialiser>()
.AddScoped<ICATALOGTenantAndUserInitialisations, CATALOGTenantAndUserInitialiser>()
// DATA Services - Initialisations.
.AddScoped<IBrandInitialiser, BrandInitialiser>()
.AddScoped<IDATACounterInitialiser, DATACounterInitialiser>()
.AddScoped<IDATAStateInitialiser, DATAStateInitialiser>()
.AddScoped<IDATASuburbInitialiser, DATASuburbInitialiser>()
.AddScoped<IStatusInitialiser, StatusInitialiser>()
.AddScoped<ITypeInitialiser, TypeInitialiser>()
.AddScoped<IVisitTypeInitialiser, VisitTypeInitialiser>()
// TESTDATA Services - Initialisations.
.AddScoped<ITESTDATAInitialisations, TESTDATAInitialisations>()
.AddScoped<ITESTDATATenantAndUserInitialisations, TESTDATATenantAndUserInitialisations>()
.AddTransient<IDATAContextFactory, DATAContextFactory>()
.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); // For getting the user.
}
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
else {
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseExceptionHandler(options => {
options.Run(async context => {
var ex = context.Features.Get<IExceptionHandlerPathFeature>();
if (ex?.Error != null) {
Debugger.Break();
}
});
});
app.UseRouting();
// global cors policy
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseFileServer();
app.UseStaticFiles();
app.UseSession();
app.UseAuthorization();
app.UseConfigureSession();
app.EnsureCATALOGMigrationAndInitialise();
app.EnsureDATAMigrationsAndInitialise();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
}
}
[汇编:NeutralResourcesLanguage(“en”)]
命名空间JobsLedger.API{
公营创业{
//private const string SecretKey=“needtogetthisfromenvironment”;
//专用只读SymmetricSecurityKey
//_signingKey=new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey));
私人只读IWebHostEnvironment;
私有只读IConfiguration\u configuration;//{get;}
公共启动(IWebHostEnvironment环境、IConfiguration配置){
_env=env;
_配置=配置;
}
//此方法由运行时调用。请使用此方法将服务添加到容器中。
public void配置服务(IServiceCollection服务){
//添加框架服务。
services.AddCors();
services.AddControllers();
services.AddOptions();
services.AddDbContext(options=>options.UseSqlServer(_configuration.GetConnectionString(“CatalogConnection”),b=>b.migrationAssembly(“JobsLedger.CATALOG”));
services.AddDbContext(options=>options.UseSqlServer(_configuration.GetConnectionString(“TenantDbConnection”),a=>a.MigrationsAssembly(“JobsLedger.DATA”));
//全面强制认证(即关闭
//关闭所有内容(除非明确打开)。
//使用策略验证。
services.AddAuthorization(选项=>{
options.AddPolicy(“TenantAdmin”,policy=>policy.requirecall(ClaimTypes.Role,“TenantAdmin”);
options.AddPolicy(“Admin”,policy=>policy.requirecall(ClaimTypes.Role,“Admin”);
options.AddPolicy(“Employee”,policy=>policy.requirecall(ClaimTypes.Role,“Employee”);
});
//services.ConfigureApplicationInjection();
//从应用程序设置中获取jwt选项
var tokenOptions=_configuration.GetSection(“身份验证”).Get();
services.AddAuthentication(x=>{
x、 DefaultAuthenticateScheme=JwtBearerDefaults.AuthenticationScheme;
x、 DefaultChallengeScheme=JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(选项=>{
options.TokenValidationParameters=新的TokenValidationParameters{
validateisuer=true,
ValidateAudience=true,
ValidateLifetime=true,
ValidateSuersigningKey=true,
RequireExpirationTime=true,
ValidIssuer=tokenOptions.Issuer,
Validudience=代币选项。观众,
IssuerSigningKey=新对称安全密钥(
Encoding.UTF8.GetBytes(tokenOptions.SigningKey)),
时钟偏移=时间跨度0
};
});
服务
.AddDistributedMemoryCache()
.AddSession()
//存储库-数据
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
//存储库-目录
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
//商业服务
//服务-API
.addScope()文件
.addScope()文件
//服务-目录
.addScope()文件
.addScope()文件
//.addScope()文件
//服务-共享
.addScope()文件
//服务-身份验证
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
//目录服务-初始化。
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
//数据服务-初始化。
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
//TESTDATA服务-初始化。
.addScope()文件
.addScope()文件
.AddTransient()
.AddSingleton();//用于获取用户。
}
公共静态无效配置(IApplicationBuilder应用程序,IWebHostEnvironment环境){
if(env.IsDevelopment()){
app.UseDeveloperExceptionPage();
}
否则{
app.UseExceptionHandler(“/Error”);
//默认的HSTS值为30天。您可能需要更改此值
[assembly: NeutralResourcesLanguage("en")]
namespace JobsLedger.API {
public class Startup {
//private const string SecretKey = "needtogetthisfromenvironment";
//private readonly SymmetricSecurityKey
// _signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey));
private readonly IWebHostEnvironment _env;
private readonly IConfiguration _configuration; // { get; }
public Startup(IWebHostEnvironment env, IConfiguration configuration) {
_env = env;
_configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) {
// Add framework services.
services.AddCors();
services.AddControllers();
services.AddOptions();
services.AddDbContext<CATALOGContext>(options => options.UseSqlServer(_configuration.GetConnectionString("CatalogConnection"), b => b.MigrationsAssembly("JobsLedger.CATALOG")));
services.AddDbContext<DATAContext>(options => options.UseSqlServer(_configuration.GetConnectionString("TenantDbConnection"), a => a.MigrationsAssembly("JobsLedger.DATA")));
// Make authentication compulsory across the board (i.e. shut
// down EVERYTHING unless explicitly opened up).
// Use policy auth.
services.AddAuthorization(options => {
options.AddPolicy("TenantAdmin", policy => policy.RequireClaim(ClaimTypes.Role, "TenantAdmin"));
options.AddPolicy("Admin", policy => policy.RequireClaim(ClaimTypes.Role, "Admin"));
options.AddPolicy("Employee", policy => policy.RequireClaim(ClaimTypes.Role, "Employee"));
});
//services.ConfigureApplicationInjection();
// Get jwt options from app settings
var tokenOptions = _configuration.GetSection("Authentication").Get<JwtTokenOptions>();
services.AddAuthentication(x => {
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
RequireExpirationTime = true,
ValidIssuer = tokenOptions.Issuer,
ValidAudience = tokenOptions.Audience,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(tokenOptions.SigningKey)),
ClockSkew = TimeSpan.Zero
};
});
services
.AddDistributedMemoryCache()
.AddSession()
// Repositories - DATA
.AddScoped<IClientDATARepository, ClientDATARepository>()
.AddScoped<ILoggingDATARepository, LoggingDATARepository>()
.AddScoped<IJobDATARepository, JobDATARepository>()
.AddScoped<IBrandDATARepository, BrandDATARepository>()
.AddScoped<ITypeDATARepository, TypeDATARepository>()
.AddScoped<IStateDATARepository, StateDATARepository>()
.AddScoped<IStatusDATARepository, StatusDATARepository>()
.AddScoped<ISuburbDATARepository, SuburbDATARepository>()
.AddScoped<ICounterDATARepository, CounterDATARepository>()
// Repositories - CATALOG
.AddScoped<ITenantCATALOGRepository, TenantCATALOGRepository>()
.AddScoped<IUserCATALOGRepository, UserCATALOGRepository>()
.AddScoped<IRoleCATALOGRepository, RoleCATALOGRepository>()
.AddScoped<ICounterCATALOGRepository, CounterCATALOGRepository>()
.AddScoped<ISuburbCATALOGRepository, SuburbCATALOGRepository>()
.AddScoped<IStateCATALOGRepository, StateCATALOGRepository>()
// Business services
// Services - API
.AddScoped<IClientServices, ClientServices>()
.AddScoped<IJobServices, JobServices>()
//Services - Catalog
.AddScoped<ITenantServices, TenantServices>()
.AddScoped<IUserServices, UserServices>()
//.AddScoped<IUserValidateService, UserValidateService>()
// Services - Shared
.AddScoped<IAddressDropdownServices, AddressDropdownServices>()
//Services - Auth
.AddScoped<ICryptoService, CryptoService>()
.AddScoped<IUserAuthorisationServices, UserAuthorisationServices>()
.AddScoped<IUserSession, UserSession>()
.AddScoped<ISessionServices, SessionServices>()
// CATALOG services - Initialisations.
.AddScoped<ICATALOGCounterInitialiser, CATALOGCounterInitialiser>()
.AddScoped<ICATALOGStateInitialiser, CATALOGStateInitialiser>()
.AddScoped<ICATALOGSuburbInitialiser, CATALOGSuburbInitialiser>()
.AddScoped<IRoleInitialiser, CATALOGRoleInitialiser>()
.AddScoped<ICATALOGTenantAndUserInitialisations, CATALOGTenantAndUserInitialiser>()
// DATA Services - Initialisations.
.AddScoped<IBrandInitialiser, BrandInitialiser>()
.AddScoped<IDATACounterInitialiser, DATACounterInitialiser>()
.AddScoped<IDATAStateInitialiser, DATAStateInitialiser>()
.AddScoped<IDATASuburbInitialiser, DATASuburbInitialiser>()
.AddScoped<IStatusInitialiser, StatusInitialiser>()
.AddScoped<ITypeInitialiser, TypeInitialiser>()
.AddScoped<IVisitTypeInitialiser, VisitTypeInitialiser>()
// TESTDATA Services - Initialisations.
.AddScoped<ITESTDATAInitialisations, TESTDATAInitialisations>()
.AddScoped<ITESTDATATenantAndUserInitialisations, TESTDATATenantAndUserInitialisations>()
.AddTransient<IDATAContextFactory, DATAContextFactory>()
.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); // For getting the user.
}
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
else {
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseExceptionHandler(options => {
options.Run(async context => {
var ex = context.Features.Get<IExceptionHandlerPathFeature>();
if (ex?.Error != null) {
Debugger.Break();
}
});
});
app.UseRouting();
// global cors policy
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseFileServer();
app.UseStaticFiles();
app.UseSession();
app.UseAuthorization();
app.UseConfigureSession();
app.EnsureCATALOGMigrationAndInitialise();
app.EnsureDATAMigrationsAndInitialise();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
}
}
/authenticate // <-- not api/authenticate
/JWTStatus // <-- not api/JWTStatus
[Authorize]
[ApiController]
//Remove the controller level route from here
public class JwtController : ControllerBase
{
[AllowAnonymous]
[Route("api/authenticate")] // <-- Here it is
[HttpPost]
public async Task<IActionResult> Authenticate([FromBody] LoginViewModel model)
{
.........
}
[Route("api/JWTStatus")] // <-- Here it is
[HttpGet]
public IActionResult GetJwtStatus()
{
.......
}
}
[Authorize]
[ApiController]
[Route("api/[controller]/[action]")] // <-- Here it is
public class JwtController : ControllerBase
{
[AllowAnonymous]
[HttpPost]
public async Task<IActionResult> Authenticate([FromBody] LoginViewModel model)
{
.......
}
[HttpGet]
public IActionResult GetJwtStatus()
{
......
}
}
api/Jwt/Authenticate
api/Jwt/GetJwtStatus
[Route("api/authenticate")]
[Route("api/JWTStatus")]