Previously, one would add something like this to Global.aspx.cs, which is gone in .NET Core:
routes.IgnoreRoute("{*favicon}", new { favicon = #"(.*/)?favicon.ico(/.*)?" });
Here's what I currently have in my Startup.cs (for .NET Core):
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
The problem is that in MVC (pre-Core) routes was a RouteCollection and in .NET Core it's a Microsoft.AspNetCore.Routing.IRouteBuilder so IgnoreRoute is not a valid method.
You could write middleware for this.
public void Configure(IApplciationBuilder app) {
app.UseDefaultFiles();
// Make sure your middleware is before whatever handles
// the resource currently, be it MVC, static resources, etc.
app.UseMiddleware<IgnoreRouteMiddleware>();
app.UseStaticFiles();
app.UseMvc();
}
public class IgnoreRouteMiddleware {
private readonly RequestDelegate next;
// You can inject a dependency here that gives you access
// to your ignored route configuration.
public IgnoreRouteMiddleware(RequestDelegate next) {
this.next = next;
}
public async Task Invoke(HttpContext context) {
if (context.Request.Path.HasValue &&
context.Request.Path.Value.Contains("favicon.ico")) {
context.Response.StatusCode = 404;
Console.WriteLine("Ignored!");
return;
}
await next.Invoke(context);
}
}
.NET Core 3.1
For .NET Core 3.1 with endpoint routing, this seems like the easiest way. You don't need to build a middleware just for this simple case.
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/favicon.ico", async (context) =>
{
context.Response.StatusCode = 404;
});
// more routing
});
The original question is about ignoring routes. If you do want to serve a favicon, you can add HTML markup in _Layout.cshtml like this. This technique allows more control over where the icon is served from.
<link rel="icon" href="#Url.Content("~/images/favicon.ico")" />
I am using both of these techniques in my code.
If you want to make a static file accessible without the routing condition, simply use the build-in StaticFiles Middleware.
Activate it with app.UseStaticFiles();
in Configure Method and put your static files in wwwroot directory.
They're availible on HOST/yourStaticFile
For more information, refer here
inside public void Configure
add
app.Map("/favicon.ico", delegate { });
Allow favicon requests to be parsed by the route handler, and keep your routes to a minimum. Avoid using middleware, this just adds additional complexity to your code and means all other requests must go through the middleware before the route handler, which is worse in terms of performance for busy websites. For websites that aren't busy you would just be wasting your time worrying about this.
See https://github.com/aspnet/Routing/issues/207
In ASP.NET Core, you can write a constrained catch-all route template. To do so, in your ASP.NET Core example, replace the call to routes.MapSpaFallbackRoute with the following:
// Returns the home/index page for unknown files, except for
// favicon.ico, in which case a 404 error is returned.
routes.MapRoute(
name: "spa-fallback",
template: "{*url:regex(^(?!favicon.ico).*$)}",
defaults: new { Controller = "Home", action = "Index" });
Related
When a request is made for an image|file it doesn't reach my middleware.
I can see that when UseCms is called it adds PiranhaStartupFilter
serviceBuilder.Services.AddTransient<IStartupFilter, PiranhaStartupFilter>();
Which adds UseStaticFiles() to the ApplicationBuilder.
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return builder =>
{
builder
.UseSecurityMiddleware()
.UseStaticFiles()
.UseMiddleware<RoutingMiddleware>()
.UseMiddleware<SitemapMiddleware>();
next(builder);
};
}
How could I overwrite this functionality so that my middleware is called for requests?
I'm expecting a call to /uploads/foo.jpg would be picked up in the InvokeAsync method of my middleware, registered like so:
app.UsePiranha(options =>
{
options.Builder.CustomImageTools();
});
At present only files & favicon requests reach the InvokeAsync method in my middleware.
As middleware in asp.net is executed in the order they are added into the pipeline it should be sufficient to add your interceptor before making any calls to Piranha.
You can add the middleware by adding a service above the call to UseCms() in startup.cs.
services.AddPiranha(options =>
{
options.CustomImageFilter();
options.UseCms();
}
public class CustomImageFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
app.UseWhen(
context => some_condition,
appbuilder => appbuilder.CustomImageTools()
);
// Call the next configure method
next(app);
};
}
}
Recently i encounter some unexpected issue
I'm using ASP.NET Core 3.0 and i defined two routes in StartUp.cs
StartUp.cs
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "file",
pattern: "{controller=File}/folder/{*path}",
new { Action = "Folder" });
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=File}/{action=Index}/{filename}");
});
FileController.cs
public class FileController : Controller
{
public IActionResult Folder(string path)
{
return Ok(path);
}
public IActionResult Index(string filename)
{
return Ok(filename);
}
}
request to file/folder/abc/abc i expected to match first route
but the result was 404 not found
but if i changed order of route
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=File}/{action=Index}/{filename}");
endpoints.MapControllerRoute(
name: "file",
pattern: "{controller=File}/folder/{*path}",
new { Action = "Folder" });
});
It work!
My questing is why first version don't work if i defined {controller=File}/folder/{*path} on top
I thought it will check route table sequentially
In general we always put customer routers before default route. For this issue if we put non catch-all route before default route it will work as expect. However if we put catch-all route before default route you will see that you could not find any pages when we access via file/folder/abc/abc. This issue is not existed in asp.net core 3.0 but also in asp.net core 2.0 and MVC project. If you need to use catch-all routes, it is better to put them later in the route talbe. You could also read this article for more details: Multiple routes. It will help to fix unknown issues.
We have an angular 5 application built on top of MVC authentication. The application is served up from the Home/Index action, and once the application is loaded, the angular routing takes care of pretty much everything. We are doing this primarily because we wanted to use MVC's Authentication processes (we are using Identity Server 4 as our Oath system).
This works well with one exception: logout. When we attempt to logout, the application seems to be immediately reauthorized and reloads instead of returning us to our Identity Server login page.
Originally, we had success in our development environment through this code:
[HttpPost]
public async Task<IActionResult> Logout()
{
foreach (string key in Request.Cookies.Keys)
{
Response.Cookies.Delete(key);
}
await HttpContext.SignOutAsync();
return Ok();
}
But it was a false positive, because all of our applications were running on localhost, so they had access to each other's cookies. Due to this, the Identity Server cookies were cleared along with the cookies for the Angular application.
We attempted to replace that logic with something like this:
public async Task Logout()
{
if (User?.Identity.IsAuthenticated == true)
{
// delete local authentication cookie
await HttpContext.SignOutAsync("Cookies");
await HttpContext.SignOutAsync("oidc");
}
}
However, it did not log out in either environment (however that code works for one of our MVC applications using the same identity server).
To give some background, our logout process for the Angular application comes from a material menu, where we then prompt the user if they really want to logout with a modal, before calling the logout function. Code snippets of this process:
Method called by logout button:
public openDialog(): void {
let dialogRef = this.dialog.open(ModalComponent, {
width: '250px',
data: { text: this.logoutText, ok: this.okText, cancel: this.cancelText }
});
dialogRef.afterClosed().subscribe(result => {
if (result !== undefined) {
switch (result.data) {
case 'ok':
this.logout();
break;
case 'cancel':
break;
default:
break;
}
}
});
}
private logout(): void {
this.sessionService.logOut();
}
Session service:
public logOut(): void {
this.auth.revokeToken();
this.http.post(this.logoutUrl, undefined).subscribe(x => window.location.reload());
}
As mentioned, calling logout this way ends up with a page refresh and the user not really being logged out. There's probably something simple we are missing, but all of the tinkering I've done so far has not led to any success.
EDIT:
Our angular routing is fairly simple (though the pathing prevents us from calling something like home/logout):
{
path: 'parts',
component: PartsComponent,
canActivate: [AuthGuard],
runGuardsAndResolvers: 'always',
data: { title: 'Parts' }
},
{
path: '',
redirectTo: 'parts',
pathMatch: 'full'
},
{
path: '**',
redirectTo: 'parts'
}
Our MVC routes are also fairly straightforward
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
"Sitemap",
"sitemap.xml",
new { controller = "Home", action = "SitemapXml" });
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
I'm not certain how we could easily route to home/logout with the angular routes the way they are. I'm guessing we'd have to add a route for it. We tried that at one point but it never routed correctly. I'm trying to find my notes on what we tried.
After you cleared the local cookies of your application send the user to the end_session_endpoint of your Idsrv. (The code you showed to clear your session should work, if not I'd the config in startup.cs and debug to check if the redirect really removed the cookie in the browser).
E.g. https://demo.identityserver.io/.well-known/openid-configuration
There you see the endpoint. This should remove the session on the Idsrv. Depending on your setup this could kill your sessions on your other applications that uses the same Identity Server instance.
You can read more about this in the docs.
I don't see a problem with the Angular routing interfering with your server side routing. As long as you ofcourse really do a redirect to that page using a regular window.location.replace
And ofcourse like #Win mentioned it would be a good security guideline to revoke refresh_token and reference_tokens if you use those.
I could not answer for Angular; I'm still working on it. However, client web app could ask IDP to revoke access token and refresh token when user signs out at client. For example, in ASP.Net Core -
using System;
using System.Threading.Tasks;
using IdentityModel.Client;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace MY_APP.Controllers
{
public class AccountController : Controller
{
[HttpGet]
public async Task<IActionResult> SignOut()
{
var discoveryClient = new DiscoveryClient("IDP_URL");
var metaDataResponse = await discoveryClient.GetAsync();
var revocationClient = new TokenRevocationClient(
metaDataResponse.RevocationEndpoint,
"CLIENT_NAME",
"CLIENT_SECRET");
// revoke the access token
string accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
if (!string.IsNullOrWhiteSpace(accessToken))
{
var revokeAccessTokenResponse = await revocationClient.RevokeAccessTokenAsync(accessToken);
if (revokeAccessTokenResponse.IsError)
throw new Exception("Problem encountered while revoking the access token.",
revokeAccessTokenResponse.Exception);
}
// revoke the refresh token
string refreshToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken);
if (!string.IsNullOrWhiteSpace(refreshToken))
{
var revokeRefreshTokenResponse = await revocationClient.RevokeAccessTokenAsync(refreshToken);
if (revokeRefreshTokenResponse.IsError)
throw new Exception("Problem encountered while revoking the refresh token.",
revokeRefreshTokenResponse.Exception);
}
return SignOut(
new AuthenticationProperties { RedirectUri = "CALL_BACK_URL" },
CookieAuthenticationDefaults.AuthenticationScheme,
OpenIdConnectDefaults.AuthenticationScheme);
}
}
}
Everytime my api is started, it's executed with LOCALHOST:PORT/api/values/. How to LOCALHOST:PORT/ with a static home page?
In your project, locate your launchSettings.json file. In visual studio you will need to expand Properties to find it from solution explorer or use Ctrl + T. This file contains an array of profiles. Each profile has a launchUrl field where you can mention your path as empty.
As of adding a content in the home page, you can always make a middleware as follows:
app.Use(async (context, _next) => {
if (string.IsNullOrEmpty(context.Request.Path.ToString())
|| context.Request.Path.ToString() == "/")
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync("Web API is now running.");
}
else
await _next();
});
You can always have an action, But I would recommend using a middleware like the above.
possible duplicate of How to set start page in dotnet core web api?
I assume you mean having a default page when the user navigates to http://localhost instead of calling http://localhost/api/controller.
In .net core 2 it's fairy easy to do. You can use static files if you only want to show a simple static page by adding
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...other code here...
app.UseDefaultFiles(new DefaultFilesOptions { DefaultFileNames = new List<string> { "index.html" } });
app.UseDefaultFiles();
app.UseStaticFiles();
}
and making sure there is an index.html in the wwwroot folder.
or you could use routing in mvc
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}");
});
see answer by aslan at https://stackoverflow.com/a/40651363/3786363
oh and unless your server is mapped to port 80 you will probably need to call localhost:port not just localhost.
Are you looking for something like this?
$.ajax({
url: "/api/values/METHOD/?PARAM=0",
type: "GET",
dataType: "json",
cache: false,
statusCode: {
200: function (data) {
//Do stuff
}
}
});
Anything running within the solution context will start at the root.
I am having a problem with ASP.NET Core not serving static files properly. I have parts of my app in node_modules under wwwroot. For the most part all files work, but there are exceptions. *.js.map files are routed to MVC controller, serving my MVC pages instead of actual files. As a result, I get errors in the browser such as
Failed to parse SourceMap: http://localhost:5000/node_modules/bootstrap/bootstrap.min.css.map
Going the same route, my web fonts, such as the one includes with Bootstrap are also not served properly, being also handled by MVC middleware instead of static files middleware. It seems that all files that reside in node_modules should be routed to my static files middleware, which is not happening.
Thanks.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine(env.WebRootPath, #"node_modules")),
RequestPath = new PathString("/node_modules"),
ServeUnknownFileTypes = true
});
app.UseMvc(config =>
{
config.MapRoute("Default", "{controller}/{action}/{id?}",
new { controller = "Home", action = "Index" });
config.MapRoute("AngularDeepLinkingRoute", "{*url}",
new { controller = "Home", action = "Index" });
});
}
The problem was that if a static file, such as *.js.map file is missing, static files middleware does not handle the request, and it goes to MVC middleware.
Remove the following the code, it is not required at all
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine(env.WebRootPath, #"node_modules")),
RequestPath = new PathString("/node_modules"),
ServeUnknownFileTypes = true
});
Use the following code only
app.UseStaticFiles();
If node_module directory is under wwwroot directory then content inside node_module will be treated as static content. Static files are stored within your project's web root directory. The default directory is /wwwroot.
For details refer to following link
Microsoft Docs - Working with static files