I'm running a .NET Core 3.1 Web API project and below is my pipeline. The problem I face is that when an error occurs, I want the error controller to be hit and the error to be logged. However, what is returned is the html from the fallback controller. Can you help me fix this routing issue?
app.UseExceptionHandler("/error");
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "KYCDB API"));
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapFallbackToController("Index", "Fallback");
});
app.UseHealthChecks("/api/monitor");
this is my error controller
[ApiController]
public class ErrorController : ControllerBase
{
private readonly ILogger<ErrorController> logger;
public ErrorController(ILogger<ErrorController> logger) => this.logger = logger;
[Route("/error")]
[NonAction]
public IActionResult Error()
{
var exception = HttpContext.Features.Get<IExceptionHandlerFeature>();
logger.LogError(exception.Error.Message, exception);
return Problem(detail: exception.Error.Message);
}
}
and the fallback controller
public class FallbackController : Controller
{
public ActionResult Index() => PhysicalFile(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "index.html"), "text/HTML");
}
Related
I have this controller
[ApiController]
[Route("api/[controller]")]
public class PlanningController: ControllerBase
{
public async Task<IActionResult> SaveTest([FromBody] TestData data)
{
return Ok(data);
}
public class TestData
{
public int Id { get; set; }
public string Name { get; set; }
}
This in Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseCors("default");
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.Run(context => context.Response.WriteAsync("Planificador API iniciada"));
}
I put a break point in the return but when I post this in postman
nothing happens in my controller the break point is not reached.
I don't understand the response received in postman
In VS 2022 I see this
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request
starting HTTP/1.1 POST
http://localhost:5001/api/planning/saveTest application/json 34
Microsoft.AspNetCore.Hosting.Diagnostics[1]
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 8.3968ms 200
Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 8.3968ms 200
Any idea, please?
Thanks
Nothing happens in my controller the break point is not reached.I
don't understand the response received in postman
Well, because of using [Route("planning")] before your PlanningController it is certainly overriding your application standard routing. So, your controller route has been changed. Thus, you shouldn't manipulate this routing [Route("api/[controller]")]
Correct Way:
[Route("api/[controller]")]
[ApiController]
public class PlanningController : ControllerBase
{
[HttpPost]
[Route("saveTest")]
public async Task<IActionResult> SaveTest([FromBody] TestData data)
{
return Ok(data);
}
}
Update:
Stratup.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.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "TestWebAPIProject", Version = "v1" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TestWebAPIProject v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Output:
Note: I would highly recommend you to have a look on our official document for Custom route constraints
Solved...
The problem was tha in my controller I was injected IFileProvider wrong
Unable to resolve service for type 'Microsoft.Extensions.FileProviders.IFileProvider' while attempting to activate my controller
I create an API Controller in my ASP.NET core web api project.
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
public MyData Method1(string Id)
{
Console.WriteLine("here");
return null;
}
public MyData Method2()
{
Console.WriteLine("here");
return null;
}
}
I set breakpoints in each method and load these urls in my browser:
https://localhost:44357/test/Method1/1343a
https://localhost:44357/test/Method2
And in my Startup.cs, I have
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
Can you please help me why my API controller does not get invoked when I load ^ urls in browser?
I read https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-6.0 , I think those method should get called.
Thank you for your help.
First check by adding verb over method [HTTPPOST] or [HTTPGET].
Also your starup.cs code looks like as written below:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
You controller Code:
[ApiController]
[Route("api/[controller]")]
public class TestController : ControllerBase
{
[HttpGet]
[Route("method1")]
public MyData Method1(string Id)
{
Console.WriteLine("here");
return null;
}
[HttpGet]
[Route("method2")]
public MyData Method2()
{
Console.WriteLine("here");
return null;
}
}
Now call you API like :
https://localhost:portno/api/test/method1/abc
https://localhost:portno/api/test/method2
Please change your route to include the action like below
[ApiController]
[Route("[controller]/[action]")]
public class TestController : ControllerBase
{
public MyData Method1(string Id)
{
Console.WriteLine("here");
return null;
}
public MyData Method2()
{
Console.WriteLine("here");
return null;
}
}
in Asp.net Api you need route for each Action:
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
[HttpGet("[Action]/{Id}")]
public MyData Method1(string Id)
{
Console.WriteLine("here");
return null;
}
[HttpGet("[Action]")]
public MyData Method2()
{
Console.WriteLine("here");
return null;
}
}
For Net Core 6.0 change this in your Program.cs
`var services = builder.Services;
services.AddControllers();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.Run();`
and [Route("methodname")] use this attribute above your action method.
I am building a .NET Core (3.1) Web API which is being hosted in IIS.
I have 2 endpoints:
/api/status
/api/widget/config/{id}
Both endpoints work perfectly when running locally. The /api/status endpoint works in my deployed version too. But the other endpoint gives a 404 error in the deployed version. As it works locally, I believe this to be an issue with how it is deployed. Please can you help me understand the issue?
Here are my 2 controllers code:
[Route("api/[controller]")]
[ApiController]
public class StatusController : ControllerBase
{
[HttpGet]
public ActionResult Get()
{
return Ok("API is available");
}
}
and
[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
private readonly IWidgetService service;
public WidgetController(IWidgetService _service)
{
service = _service;
}
[HttpGet]
[Route("~/api/[controller]/[action]/{id}")]
public ActionResult Config(Guid id)
{
return Ok(service.GetWidgetConfig(id));
}
}
and below is my Program.cs and Startup.cs:
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
SeedDatabase.Initialize(services);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occured seeding the DB");
}
}
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel();
webBuilder.UseContentRoot(Directory.GetCurrentDirectory());
webBuilder.UseIIS();
webBuilder.UseStartup<Startup>();
});
and
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(opts => opts.UseSqlServer(Configuration.GetConnectionString("sqlConnection"),
options => options.MigrationsAssembly("MyProject")));
services.AddIdentity<ApplicationUser, IdentityRole>(opt =>
{
opt.Password.RequiredLength = 8;
opt.Password.RequireDigit = true;
opt.Password.RequireUppercase = true;
opt.Password.RequireNonAlphanumeric = true;
opt.SignIn.RequireConfirmedAccount = false;
opt.SignIn.RequireConfirmedAccount = false;
opt.SignIn.RequireConfirmedPhoneNumber = false;
}).AddEntityFrameworkStores<ApplicationDbContext>();
services.AddScoped<IWidgetService, WidgetService>();
services.AddCors(o => o.AddPolicy("CorsPolicy", builder => {
builder
.WithMethods("GET", "POST")
.AllowAnyHeader()
.AllowAnyOrigin();
}));
services.AddMvc()
.AddNewtonsoftJson(options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/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.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors("CorsPolicy");
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Change your controller code to this:
[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
private readonly IWidgetService service;
public WidgetController(IWidgetService _service)
{
service = _service;
}
[HttpGet("Config/{id}")]
public ActionResult Config(Guid id)
{
return Ok(service.GetWidgetConfig(id));
}
}
Change your code like below:-
Startup.cs
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
Controller:-
[ApiController]
[Route("api/[controller]")]
public class WidgetController : ControllerBase
{
private readonly IWidgetService service;
public WidgetController(IWidgetService _service)
{
service = _service;
}
[HttpGet("Config/{id}")]
public ActionResult Config(Guid id)
{
return Ok(service.GetWidgetConfig(id));
}
}
Also try your write connection string in appsettings.Development.json file.
It will resolve your issue.
I am following an video tutorial for identity server 4 with web api's.
And Im not sure when I went wrong.
Im getting 401 Unauthorized when I try to call api with bearer token.
In previos step, without authorization, my api worked.
This is my api controller in my TablesReach.API project:
...
namespace TablesReach.Controllers
{
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly DataContext _context;
public UsersController(DataContext context)
{
_context = context;
}
// GET: api/Users
[HttpGet]
public async Task<ActionResult<IEnumerable<User>>> GetUsers()
{
return await _context.Users.ToListAsync();
}
...
this is my Startup.cs of my api project:
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.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(opts =>
{
opts.Authority = "http://localhost:5000";
opts.RequireHttpsMetadata = false;
opts.ApiName = "TablesReachApi";
});
services.AddDbContext<DataContext>(opts => opts.UseInMemoryDatabase("UNWDb"));
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseAuthentication();
}
}
My other project TablesReach.IdentityServer is host on localhost:5000
and Im being able to get bearer token, so I assume that this project is quite OK.
identityServer startup.cs class:
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.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiScopes(Config.GetAllApiResources())
.AddInMemoryClients(Config.GetClients());
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//else
//{
// app.UseExceptionHandler("/Home/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.UseHttpsRedirection();
//app.UseStaticFiles();
//app.UseRouting();
//app.UseAuthorization();
//app.UseEndpoints(endpoints =>
//{
// endpoints.MapControllerRoute(
// name: "default",
// pattern: "{controller=Home}/{action=Index}/{id?}");
//});
app.UseIdentityServer();
}
}
and Config.cs:
public class Config
{
public static IEnumerable<ApiScope> GetAllApiResources()
{
return new List<ApiScope>
{
new ApiScope("TablesReachApi", "Api for solution")
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = { "TablesReachApi" }
}
};
}
}
Note: When I remove annotation [Authorize] from my api controller I can reach my method.
For some middleware, order matters. Authentication and authorization, for example, can't go in the order that you have put them in the API. Microsoft has some clear documentation on this for you to read here..
We have an existing .NET 5.0 Blazor web application. I have added an ASP.NET Core Web API to the same project because I want to provide a REST interface to external consumers. When I request http://localhost:5000/stripe/customerwebhook, I get a 404 not found error. What could I be missing?
My CustomerWebhookController API class looks like the following:
[Route("stripe/[controller]")]
[ApiController]
public class CustomerWebhookController : ControllerBase
{
[HttpPost]
public async Task<IActionResult> Index()
{
return Ok();
}
}
Startup.cs:
using Microsoft.AspNetCore.Mvc;
public partial class Startup
{
public void ConfigureServices(IServiceCollection services)
{
OnConfiguringServices(services);
services.AddHttpContextAccessor();
services.AddScoped<HttpClient>(serviceProvider =>
{
var uriHelper = serviceProvider.GetRequiredService<NavigationManager>();
return new HttpClient
{
BaseAddress = new Uri(uriHelper.BaseUri)
};
});
services.AddHttpClient();
services.AddAuthentication();
services.AddAuthorization();
services.AddControllersWithViews();
services.AddMvc(options => options.EnableEndpointRouting = false)
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddRazorPages();
services.AddServerSideBlazor().AddHubOptions(o =>
{
o.MaximumReceiveMessageSize = 10 * 1024 * 1024;
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ApplicationIdentityDbContext identityDbContext)
{
OnConfiguring(app, env);
if (env.IsDevelopment())
{
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
app.UseDeveloperExceptionPage();
}
else
{
app.Use((ctx, next) =>
{
return next();
});
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseMvcWithDefaultRoute();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}