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();
});
Related
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]
I am attempting to change my start page in my ASP.NET Core MVC C# app. I want to first take the user to a login page and I have changed in the Startup.cs to this:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Login}/{action=Index}/{id?}");
}
}
and my controller looks like this
public class LoginController : Controller
{
public IActionResult Index()
{
return View();
}
}
And I have a page called Login.cshtml
What am I missing here?
This is my startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using eDrummond.Models;
namespace eDrummond
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<eDrummond_MVCContext>();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(opetions =>
{
options.LoginPath = "/Login/Index";
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseAuthentication();
app.UseCors(
options => options.AllowAnyOrigin()
);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Login}/{action=Index}/{id?}");
});
}
}
}
I am using VS2019 and created an asp.net core app, then selected MVC and all I've done is Scaffold Tables. SO the config should be correct?
You need to take regard of an authentication flow. First you are unauthorized. When you access any page where you aren't authorized you would like to REDIRECT to your login page, right? Then you need to tell this the program:
public void ConfigureServices(IServiceCollection services) {
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
// What kind of authentication you use? Here I just assume cookie authentication.
.AddCookie(options =>
{
options.LoginPath = "/Login/Index";
});
}
public void Configure(IApplicationBuilder app) {
// Add it but BEFORE app.UseEndpoints(..);
app.UseAuthentication();
}
Here is a stackoverflow topic, that addresses your problem:
ASP.NET core, change default redirect for unauthorized
Edit:
It turns out, that you can do something like this:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// You need to comment this out ..
// services.AddRazorPages();
// And need to write this instead:
services.AddMvc().AddRazorPagesOptions(options =>
{
options.Conventions.AddPageRoute("/Login/Index", "");
});
}
2. Edit:
So my first answer was not wrong, but it it did not included the changes to the controller it would need.
There are two solutions:
You add [Authorize] to all controller, you want to be authorized,
for example IndexModel (located in /Pages/Index.cshtml.cs) and when
entered, the program would see, the user is not authorized and would
redirect to /Login/Index (file located in
/Pages/Login/Index.cshtml.cs). Then you don't need to specify a DefaultPolicy or a FallbackPolicy (see source code below for FallbackPolicy reference)
You can make the program say, that all controller need to be
authorized even when not marked by [Authorize]. But then you need to
mark those controller you want pass without authorization with
[AllowAnonymous]. This is how it should be implemented then:
Structure:
/Pages
Index.cshtml
Index.cshtml.cs
/Login
Index.cshtml
Index.cshtml.cs
/Startup.cs
Files:
// File located in /Startup.cs:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.Configuration;
namespace stackoverflow_aspnetcore_59448960
{
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)
{
// Does automatically collect all routes.
services.AddRazorPages();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
// That will point to /Pages/Login.cshtml
options.LoginPath = "/Login/Index";
}); ;
services.AddAuthorization(options =>
{
// This says, that all pages need AUTHORIZATION. But when a controller,
// for example the login controller in Login.cshtml.cs, is tagged with
// [AllowAnonymous] then it is not in need of AUTHORIZATION. :)
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
}
// 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("/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 =>
{
// Defines default route behaviour.
endpoints.MapRazorPages();
});
}
}
}
// File located in /Pages/Login/Index.cshtml.cs:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace stackoverflow_aspnetcore_59448960.Pages.Login
{
// Very important
[AllowAnonymous]
// Another fact: The name of this Model, I mean "Index" need to be
// the same as the filename without extensions: Index[Model] == Index[.cshtml.cs]
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public void OnGet()
{
}
}
}
// File located in /Pages/Index.cshtml.cs
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace stackoverflow_aspnetcore_59448960.Pages
{
// No [Authorize] needed, because of FallbackPolicy (see Startup.cs)
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public void OnGet()
{
}
}
}
You should have folder structure as shown below.
Views
Login
Index.cshtml
It should take you to login page by default.
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.
I am trying to setup a Blazor Server side app, but running into an issue with the app reading data from my MVC Controller API. I have a controller for my model Study called StudyController. I can access the json data for my GetAll() route "/studies" when I launch the Blazor app, but the Blazor app is not reading the data. Code below:
StudyController:
[Route("studies")]
[ApiController]
public class StudyController : ControllerBase
{
private StudyRepository _ourCustomerRespository;
public StudyController()
{
_ourCustomerRespository = new StudyRepository();
}
//[Route("Studies")]
[HttpGet]
public List<Study> GetAll()
{
return _ourCustomerRespository.GetStudies();
}
}
Razor page function section trying to access data:
#functions {
IList<Study> studies = new List<Study>();
protected async Task OnInitAsync()
{
HttpClient Http = new HttpClient();
studies = await Http.GetJsonAsync<List<Study>>("/studies");
}
}
Startup.cs configuration code:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options => options.EnableEndpointRouting = false)
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddControllers();
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
}
// 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
{
// 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.UseMvcWithDefaultRoute();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
It appears the issue was that OnInitAsync() no longer works in the latest version of Blazor. I switched to using OnInitializedAsync() and that data loaded correctly.
You Can get any exception like
"An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set."
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