I have an .NET Core 5 with Angular app and I have my controllers grouped in areas. I made the app using NET Core 1 and have successfully migrated it up to 5 without any problems, but migrating it to NET 6 gives me a 404 errors when I make API calls.
My current NET 5 setup looks like this:
[Authorize]
[ApiController]
[Route("[area]/[controller]/[action]")]
public abstract class BaseController : Controller
{
}
[Area("Home")]
public abstract class HomeController : BaseController
{
}
public class AccountController : HomeController
{
[HttpGet]
public async Task<IActionResult> GetSomething()
{
}
I created a new project in VS2022, copied everything, made the changes in Program.cs and changed BaseController to inherit ControllerBase.
The angular app works OK, but all my API calls return 404.
I didn't even have this in the NET 5 app, but I added it now just in case:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name : "areas",
pattern : "{area:exists}/{controller=Home}/{action=Index}/{id?}"
);
});
but still no luck.
EDIT:
Using this answer, I listed all the routes and do get this:
{area: 'Home', controller: 'Account', action: 'GetSomething', name: null, template: 'Home/Account/GetSomething'}
and I still have no idea why it doesn't work.
Because of the proxy you have to list all the areas PROXY_CONFIG in proxy.conf.js. Add the area, or if you're using controllers, the controller names to the context property, eg:
context: ['/AdminApi', '/HomeApi' ... ]
Or, as a workaround, add /Api before all your calls and then have just that in the proxy settings
I am trying to add an MVC, or WebAPI, or whatever controller to my Blazor Server project.
I have read numerous SO questions, blogs and etc. on the matter, like this. None of them work.
No matter what I add to my "endpoints" or "app" in my "Configure" or "ConfigureServices" methods when I start my application in debug and try to make a request using Postman it times out.
I tried:
Adding controller exactly as shown in the linked answer.
Adding any and all of those in my Startup:
endpoints.MapControllers()
endpoints.MapGet("/aaa", async context => await context.Response.WriteAsync("Hello World!"););
endpoints.MapDefaultControllerRoute()
app.UseMvc()
app.UseMvcWithDefaultRoute()
Neither does Postman get any answer (times out) nor does a breakpoint I've set in the "Get" method of the controller get triggered.
What can I do to get a controller working?
I did the following (which "worked on my machine"):
create a new BlazorApp:
dotnet new blazorserver -o BlazorApp --no-https
add a controller:
namespace BlazorApp.Controllers
{
using Microsoft.AspNetCore.Mvc;
[ApiController]
public class TestController : ControllerBase
{
[HttpGet("test")]
public ActionResult<string> Test()
{
return "TODO";
}
}
}
add endpoints.MapControllers(); to Satrtup.cs
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers(); // new
endpoints.MapBlazorHub(); // existing
endpoints.MapFallbackToPage("/_Host"); // existing
});
run the app
go to http://localhost:5000/test (you'll get the "TODO" response)
go to http://localhost:5000 (you'll see the Blazor page)
EDIT
#Tessaract in the comments mentioned that the site was running on HTTPS while the plain HTTP was being queried (and therefor didn't work).
I know is too late, but meaby helpfull somenone in future.
Check add Controllers in Program.cs like this
builder.Services.AddControllers();
When builder is
var builder = WebApplication.CreateBuilder(args);
And dont forget about this:
var app = builder.Build();
app.MapControllers();
app.Run();
I recently upgraded my project to .net-core 3.1 I noticed some bizarre behavior, all of my authenticated controllers 404.
I've left a default anonymous endpoint which I generally just use to verify that my api is working.
This controller is working just fine.
[AllowAnonymous]
[Route("api/[controller]")]
public class ValuesController : Controller
{
//...
}
This controller seems to work fine, despite not being decorated with the [ApiController]
I've found a related issue which state that this is related to the ApiVersioning
However I don't see a way to set that in the startup in .Net-Core 3.1
I've added [ApiController] to all of the authenticated controllers, the only affect I saw from this is that all of my Public Methods are now eligible to validations of the number of parameters allow to bind to the body. Everything still 404's
[ApiController]
[Route("api/[controller]")]
public class AccountController : Controller
{
//...
HttpPost("ExternalLogin")]
[AllowAnonymous]
public IActionResult ExternalLogin(string provider, string entryCode = null, string returnUrl = null)
{
//...
}
}
How can I get my controllers to receive the requests?
If you migrated from .NET Core 2.2 to 3.1, you will have to adjust Startup class:
In ConfigureServices:
Replace services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
with
services.AddControllers();
And in the Configure method:
Instead of
app.UseMvc();
Use:
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
I have a Blazor WebAssembly solution with a client project, server project and shared project, based on the default solution template from Microsoft. I'm editing and debugging in Visual Studio 2019 preview with Google Chrome.
Out-of-the-box, the solution has a single start-up project, which is the server application. That server application has a project reference to the client application. You can set it to use HTTPS by checking "Enable SSL" in the server project properties and I have done that.
When you click on debug it works perfectly.
Now I want to change it so that the Blazor WASM app only responds to requests from https://localhost:44331 and not requests to https://localhost:44331/api. These requests should be dealt with by API Controller endpoints of the server application instead. So, if somebody visits https://localhost:44331/api/something, and no such API endpoint exists, they should receive a 404 error code from the API and not be routed to the usual Blazor page saying "Sorry, there's nothing at this address."
I want to use this extra "/api" portion of the URL to keep the requests to the API separate from requests for pages. I think this will be closer to how a normal setup would be in production. I hope it's clear what I'm trying to do.
Here is a sample Controller declaration with route attribute:
namespace BlazorApp2.Server.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class WeatherForecastController : ControllerBase
{
// Etc.
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
//etc.
}
///etc.
}
}
Here is what I have tried in my Startup.cs and it does not work. Can anybody suggest something that will please?
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// Etc.
app.UseStatusCodePages();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
// The line commented out below is the out-of-the-box behaviour for a Blazor WASM app with ASP NET Core API. This is the line I want to replace.
// endpoints.MapFallbackToFile("index.html");
// The line below is my (failed) attempt to get the behaviour I want.
endpoints.MapFallback(HandleFallback);
});
}
private async Task HandleFallback(HttpContext context)
{
var apiPathSegment = new PathString("/api"); // Find out from the request URL if this is a request to the API or just a web page on the Blazor WASM app.
bool isApiRequest = context.Request.Path.StartsWithSegments(apiPathSegment);
if (!isApiRequest)
{
context.Response.Redirect("index.html"); // This is a request for a web page so just do the normal out-of-the-box behaviour.
}
else
{
context.Response.StatusCode = StatusCodes.Status404NotFound; // This request had nothing to do with the Blazor app. This is just an API call that went wrong.
}
}
Does anybody know how to get this working how I'd like, please?
To recap the problem, when somebody makes a request to:
https://yourapp.com/api/someendpoint
and /api/someendpoint can't be found, they're taken to a Blazor page. This default behaviour is weird. For requests starting with /api, they were expecting an HTTP Status Code and probably a JSON object too, but instead, they got HTML. Maybe they don't even use your app. Maybe they're not even human (more likely they're a piece of software).
This is how you send them an HTTP Status Code instead.
On your controllers:
[Route("api/[controller]")]
public class SampleController : ControllerBase
{
// ...
}
In Startup.cs:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.Map("api/{**slug}", HandleApiFallback);
endpoints.MapFallbackToFile("{**slug}", "index.html");
});
}
private Task HandleApiFallback(HttpContext context)
{
context.Response.StatusCode = StatusCodes.Status404NotFound;
return Task.CompletedTask;
}
Pretty sure this should work:
endpoints.MapFallbackToFile("{*path:regex(^(?!api).*$)}", "index.html"); // don't match paths beginning with api
I think it means something like only match URLs where the path does not start with api.
You can fix this by explicitly mapping Blazor fallback only for paths that don't start with /api and, then only mapping api paths for those that do start with /api, like I mention in this answer to my owner question. This gives the benefit that instead of just returning a 404 if you try to POST to a GET api method, you will get the proper api response of 405, or whatever other error the api would normally return given the request.
//explicitly only use blazor when the path doesn't start with api
app.MapWhen(ctx => !ctx.Request.Path.StartsWithSegments("/api"), blazor =>
{
blazor.UseBlazorFrameworkFiles();
blazor.UseStaticFiles();
blazor.UseRouting();
blazor.UseEndpoints(endpoints =>
{
endpoints.MapFallbackToFile("index.html");
});
});
//explicitly map api endpoints only when path starts with api
app.MapWhen(ctx => ctx.Request.Path.StartsWithSegments("/api"), api =>
{
//if you are not using a blazor app, you can move these files out of this closure
api.UseStaticFiles();
api.UseRouting();
api.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
});
Using code from #Darragh I get the following error:
endpoints.MapFallbackToPage("{path:regex(^(?!api).$)}", "index.html");
System.ArgumentException: ''index.html' is not a valid page name. A
page name is path relative to the Razor Pages root directory that
starts with a leading forward slash ('/') and does not contain the
file extension e.g "/Users/Edit". (Parameter 'pageName')'
The code will run if I use MapFallbackToFile instead of MapFallbackToPage like the original code.
However when I tested the regex it matched everything including an API URL:
https://regex101.com/r/nq7FCi/1
My Regex would look like this instead: ^(?!.*?(?:\/api\/)).*$ based on this answer:
https://stackoverflow.com/a/23207219/3850405
https://regex101.com/r/qmftyc/1
When testing this out It did not work anyway and urls containing /api/ was redirected to index.html.
My final code is based on #benjamin answer but with the original MapFallbackToFile used last.
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.Map("api/{**slug}", HandleApiFallback);
endpoints.MapFallbackToFile("index.html");
});
private Task HandleApiFallback(HttpContext context)
{
context.Response.StatusCode = StatusCodes.Status404NotFound;
return Task.CompletedTask;
}
I have tried this with Blazor WebAssembly .NET 5. After publishing to IIS, the previously suggested solutions do not work.
The answer to this question is provided here. Tested and working.
Shortly:
Edit wwwroot\service-worker.published.js file and add the path to exclude, in this case /api/.
const shouldServeIndexHtml = event.request.mode === 'navigate' &&
!event.request.url.includes('/api/')
If you start from a Blazor WASM hosted solution, you'll a get a sample, just launch
dotnet new blazorwasm --hosted
It create a solution with 3 projects :
|-- Client
|-- Server
|-- Shared
In The Server Startup class, the middleware pipeline is setup like this :
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebAssemblyDebugging();
}
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.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapFallbackToFile("index.html");
});
}
The controller define its route with Route attribute. If you want to host the controller at /api/{controller} use the Route attribute value api/[controller]:
[ApiController]
[Route("api/[controller]")]
public class WeatherForecastController : ControllerBase
With all other solutions applied, I still encounter the problem in ASP.NET Core 7 (not sure if the version matters) and it only happens on Production server. Turned out there's this difference between dev and production environment:
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error"); // <-- This line
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
Removing app.UseExceptionHandler and I can see exceptions again.
When i am adding route "/Poster/animals_institute/animals_in_trees/research_about_frogs_in_trees" and redirecting to "Dashboard" page using following code then it's working fine.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddRazorPagesOptions(options =>
{
options.Conventions.AuthorizePage("/Dashboard");
options.Conventions.AddPageRoute("/Dashboard", "/Game/animals_institute/animals_in_trees/research_about_frogs_in_trees");
});
}
But that's static routing i need to change it with dynamic routes and access these route value in .cs file. Like routes can be following:
/Game/animals_institute/animals_in_trees/research_about_frogs_in_trees
/Game/birds_institute/birds_on_trees/research_about_dove
/Game/animals_institute/know/know_about_cat_in_trees
/Game/animals_institute/show/show_cat_results
/Game/men_institute/men_in_society/know_about_man_in_hospital
How can i do this type of custom routing in asp.net core 2.1 identity ui
Just add route using AddPageRoute method like the following code:
options.Conventions.AddPageRoute("/Game", "/Game/{client}/{event}/{title}");
and you can pass any link like
"http://localhost:64643/Game/animals_institute/animals_in_trees/research_about_frogs_in_trees"
or
"http://localhost:64643//Game/birds_institute/birds_on_trees/research_about_dove"