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

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

Related

ASP.NET Core weird URL parsing. Single query string parameter

I want to pass a string parameter to an action. Acreated a method in the HomeController with the following signature:
[HttpGet]
public IActionResult TestView([FromQuery] string test)
{
return View(test);
}
This is my configuration 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.AddControllersWithViews();
}
// 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?}");
});
}
}
When I go to https://localhost:5001/Home/TestView it works ok
When I add a query string ?test=myvalue it fails to find the view. It tries to locate the view using weird paths.
InvalidOperationException: The view 'myvalue' was not found. The following locations were searched:
/Views/Home/myvalue.cshtml
/Views/Shared/myvalue.cshtml
Is that a bug?
The behavior is occurring because you passed the value "myvalue" as the first parameter of your returned ViewResult, which is the viewname parameter:
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.viewresult.viewname?view=aspnetcore-5.0
If you change your return statement to:
return View();
Then you won't be passing a view name parameter and it will then search for a view named TestView.

ASP.NET Core 3 API routing

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.

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.

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

Categories