ASP.NET Core 3 API routing - c#

So I've created an API project using .NET Core 3 and inside the project I've created a Controller like so:
[Route("api/account")]
[ApiController]
public class AccountController : ControllerBase
{
public IActionResult Hello()
{
return Ok("Hello");
}
}
In my Startup.cs I have:
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
From what I understand, the line services.AddControllers(); picks up every controller in my api project. I remember in asp.net to add controllers you would have to call this line:
services.AddTransient<AccountController>();
You would have to namely add each controller, is there no way to do this in .NET Core 3?

If you want that certain endpoints should not be hit, MVC provide provision to use attribute [NonAction]:
[NonAction]
public IActionResult Index()
The end points for which the attribute is used, would not be hit in the API call.

If you don't want to expose an action method(end point) to client, you can make the method as "private" or as Imran suggested, you can decorate the method with "[NonAction]" attribute.

Related

Swagger, open api with .net Core 3.0

I have a .net core 3.0 application and I am trying to implement Swashbuckle package . So I can do a http get request.
I have a controller like this:
[Route("api/products")]
[ApiController]
public class ProductValuesController : Controller
{
private DataContext context;
public ProductValuesController(DataContext data)
{
this.context = data;
}
[HttpGet("{id}")]
public Product GetProduct(long id)
{
return context.Products.Find(id);
}
public IActionResult Index()
{
return View();
}
}
and the startup.cs file looks like this:
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)
{
string connectionString = Configuration["ConnectionStrings:DefaultConnection"];
services.AddDbContext<DataContext>(options =>
options.UseSqlServer(connectionString));
services.AddControllersWithViews();
services.AddRazorPages();
services.AddSwaggerGen(options => {
options.SwaggerDoc("v1", new OpenApiInfo { Title = "SportsStore", 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, IServiceProvider services)
{
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.UseSwagger();
app.UseSwaggerUI(options => {
options.SwaggerEndpoint("/swagger/v1/swagger.json",
"SportsStore API");
});
// SeedData.SeedDatabase(services.GetRequiredService<DataContext>());
}
}
But if I start the application and browse to:
https://localhost:5001/swagger/v1/swagger.json
I will see this error:
NotSupportedException: Ambiguous HTTP method for action - ServerApp.Controllers.ProductValuesController.Index (ServerApp). Actions require an explicit HttpMethod binding for Swagger 2.0
So my quesiton is: what I have to change, so that it will work?
Thnak you.
I had this issue as well, it looks like the IActionResult Index() is causing the issue. You can do as mentioned above and decorate it with [NonAction] attribute and then it should fix it.
Decorate your public non - REST methods in the Controller as [NoAction]

Authorize only controller allows anonymous access in .net core

I have setup identity in a .net core web app, and marked a certain controller as authorize like this..
[Authorize(Roles = "Partner")]
public class ClaimsController : Controller
{
[Authorize(Roles = "Partner")]
public IActionResult Index()
{
var authenticated = User.Identity.IsAuthenticated;
//authenticated is false - but this view still loads?!
return View();
}
}
So only users in the partner role should have access.. However someone not logged in at all can load and view the Index view on the claims controller.. I could check if someone is logged in and check the role user explicitly with the user manager but surely these attributes should do something?
Is there something extra I need in startup.cs in core 3? This is my startup.cs file..
public class Startup
{
private readonly IConfiguration _config;
public Startup(IConfiguration config)
{
_config = config;
}
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
var connstring = _config.GetConnectionString("HP_RBS_Database");
//we can create our own role and derive from IdentityRole
services.AddIdentity<UserLogin, IdentityRole>(x =>
{
x.User.RequireUniqueEmail = true;
//set password rules in here..
}) //specify where we store identity data
.AddEntityFrameworkStores<HP_RBS_Context>();
services.AddMvc();
services.AddRazorPages();
services.AddControllersWithViews().AddRazorRuntimeCompilation();
services.AddDbContext<HP_RBS_Context>(x =>
{
x.UseSqlServer(connstring);
});
services.AddTransient<HPPartnerPortalSeeder>();
services.AddScoped<IHP_RBS_Repository, HP_RBS_Repository>();
services.AddAuthentication();
services.AddAuthorization();
}
// 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.UseStaticFiles();
app.UseAuthentication();
app.UseAuthorization();
app.UseRouting();
app.UseEndpoints(x =>
{
x.MapControllerRoute("Default",
"{controller}/{action}/{id?}",
new { controller = "Home", action = "Index" });
});
}
}
The calls to UseAuthentication and UseAuthorization must be placed between UseRouting and UseEndpoints:
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(x =>
{
x.MapControllerRoute("Default",
"{controller}/{action}/{id?}",
new { controller = "Home", action = "Index" });
});
When these calls are placed before UseRouting, the UseAuthorization call is somewhat of a no-op. It checks to see whether an endpoint has been selected, but this hasn't happened yet. The selection process is performed courtesy of the UseRouting call that runs next, which is too late.
Unfortunately, this means that the MVC endpoint runs as though authorisation succeeded, eventhough it wasn't performed at all. This is a known issue in the 3.0.0 release of ASP.NET Core, which has been fixed in the 3.0.1 release.

Attribute routing for simple ping controller with Get() not working

I have simple asp.net core web api with a nudge controller that returns true for Get request. The PingController.cs looks as follows:
[Route("api/[Controller]")]
public class PingController : Controller
{
[HttpGet]
public IActionResult Get()
{
return Ok(true);
}
}
Why is navigating to the controller (http://localhost:56103/api/Ping) returning 404 ?
I added route on top of the controller and the HttpMethod for specific action. What is it that I am missing or not understanding here ?
When I add app.UseMvcWithDefaultRoute() in Startup.cs the controller works fine. (This is also confusing me.)
Startup.cs looks like the following :
public class Startup
{
public IConfiguration Configuration { get; set; }
public Startup()
{
Configuration = BuildConfiguration();
}
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
...
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
ConfigureRoutes(app);
}
private static void ConfigureMvc(IServiceCollection services, Config config)
{
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)// auto generated
.AddJsonOptions(options => { options.SerializerSettings.Formatting = Formatting.Indented; })
.AddControllersAsServices();
}
private static void ConfigureRoutes(IApplicationBuilder app)
{
//app.UseMvcWithDefaultRoute();
}
}
You need to add UseMvc() or UseMvcWithDefaultRoute() in startup to define routing.
UseMvcWithDefaultRoute adds a default route named 'default' to the request execution pipeline and equals to
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});

ASP.NET Core 2.2 Returning 404 for every other Controller

I'm working on an ASP.NET Core Application and one of my Controllers works fine. But every controller after that does not seem to work, returning 404.
This controller responds as expected:
namespace App.Controllers {
[Route("api/[controller]")]
public class AccountController : CustomControllerBase {...}
}
But this controller, and all others, return 404:
namespace App.Controllers {
[Authorize]
[Route("api/[controller]")]
public class UserController : CustomControllerBase {...}
}
CustomControllerBase looks like this:
namespace App.Controllers {
[ApiController]
public class CustomControllerBase : ControllerBase {
public CustomControllerBase() {
}
}
}
And my Startup.cs looks like this:
namespace App {
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.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddDbContext<DatabaseContext>(options => options.UseMySql(Configuration.GetConnectionString("MySQLConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>().AddEntityFrameworkStores<DatabaseContext>();
services.AddScoped<IUserService, UserService>();
services.Configure<JWTSettings>(Configuration.GetSection("JWT"));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
} else {
// 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.UseMvc(routes => {
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}"
);
});
app.UseAuthentication();
}
}
}
Any ideas? I've been stuck on this for a few days and I cannot figure out what I am doing wrong.
Thanks

Routing to an ApiController in a razor page app?

I created a ASP.NET Core Razor page app (asp.net version 2.1.1). It works just fine with the normal Pages but I also want an ApiController as in this tutorial:
https://learn.microsoft.com/en-us/aspnet/core/tutorials/web-api-vsc?view=aspnetcore-2.1
However, when I create my controller just as in the example above, I get a 404 page whenever I try to reach it.
Is there something I am missing from the startup class?
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.AddDbContext<DomainDbContext>(opt => opt.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc();
}
And my apicontroller class:
[Route("api/[controller]")]
[ApiController]
class DomainController : ControllerBase
{
private readonly DomainDbContext _context;
public DomainController (DomainDbContext context)
{
_context = context;
}
[HttpGet]
public ActionResult<List<Domain>> GetAll()
{
return new List<Domain> {new Domain() {Name ="Hello", Tld = ".se", Expiration = DateTime.UtcNow.AddDays(35)}};
}
}
Everything looks like the guides as far as I can see, but obviously something is not correct since I get 404 for all pages. Even if I create a new method it doesn't really quite work as intended and is unreachable.
The main path I've tried is /api/domain.
Thanks for your help in advance!
You need to have a public controller class.
So instead of:
[Route("api/[controller]")]
[ApiController]
class DomainController : ControllerBase
{
[...]
}
You should have this:
[Route("api/[controller]")]
[ApiController]
public class DomainController : ControllerBase // <-- add a public keyword
{
[...]
}
Just add endpoints.MapControllers() to the UseEndpoints options:
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
});

Categories