Rewrite URL in ASP.Net Core Razor Pages (not MVC) - asp.net-core

By seo friendly, I am trying to use the rewrite middleware to rewrite an url to a seo friendly url, but I am not successful.
As an example, what I want to do is rewrite the url https://example.com/1 to https://example.com/test-1 and the url https://example.com/1/2 to https://example.com/test-1/test-2.
I leave the Startup and ChangeURL classes that I have made.
Startup.cs:
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.AddRazorPages();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var options = new RewriteOptions();
options.Rules.Add(new ChangeUrl());
app.UseRewriter(options);
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 =>
{
endpoints.MapRazorPages();
});
}
}
ChangeURL.cs:
public class ChangeUrl : IRule
{
public void ApplyRule(RewriteContext context)
{
var request = context.HttpContext.Request;
Match m1 = Regex.Match(request.Path.Value, #"^/(\d+)");
Match m2 = Regex.Match(request.Path.Value, #"^/(\d+)/(\d+)");
if (m1.Success)
{
request.Path = "/test-1";
}
else if (m2.Success)
{
request.Path = "/test-1/test-2";
}
context.Result = RuleResult.ContinueRules;
return;
}
}
I appreciate all the help you can give me.

As far as I know, asp.net core contains the url rewrite module which is used to url rewrite it.
More details, you could refer to below codes:
// Add below codes into the Configure method
var options = new RewriteOptions()
.AddRewrite(#"^(\d+)/(\d+)", "test-$1/test-$2",
skipRemainingRules: true)
.AddRewrite(#"^(\d+)", "test-$1",
skipRemainingRules: true);
app.UseRewriter(options);
More details, you could refer to this artcle.
public void ApplyRule(RewriteContext context)
{
var request = context.HttpContext.Request;
Match m1 = Regex.Match(request.Path.Value, #"^/(\d+)");
Match m2 = Regex.Match(request.Path.Value, #"^/(\d+)/(\d+)");
if (m1.Success)
{
request.Path = "/home/Privacy";
}
if (m2.Success)
{
request.Path = "/home/account";
}
context.Result = RuleResult.ContinueRules;
return;
}

Related

.net core 5.0.2 and jwt => response 401 Unauthorized

I am following an video tutorial for identity server 4 with web api's.
And Im not sure when I went wrong.
Im getting 401 Unauthorized when I try to call api with bearer token.
In previos step, without authorization, my api worked.
This is my api controller in my TablesReach.API project:
...
namespace TablesReach.Controllers
{
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly DataContext _context;
public UsersController(DataContext context)
{
_context = context;
}
// GET: api/Users
[HttpGet]
public async Task<ActionResult<IEnumerable<User>>> GetUsers()
{
return await _context.Users.ToListAsync();
}
...
this is my Startup.cs of my api project:
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.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(opts =>
{
opts.Authority = "http://localhost:5000";
opts.RequireHttpsMetadata = false;
opts.ApiName = "TablesReachApi";
});
services.AddDbContext<DataContext>(opts => opts.UseInMemoryDatabase("UNWDb"));
services.AddControllers();
}
// 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.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseAuthentication();
}
}
My other project TablesReach.IdentityServer is host on localhost:5000
and Im being able to get bearer token, so I assume that this project is quite OK.
identityServer startup.cs 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.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiScopes(Config.GetAllApiResources())
.AddInMemoryClients(Config.GetClients());
}
// 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?}");
//});
app.UseIdentityServer();
}
}
and Config.cs:
public class Config
{
public static IEnumerable<ApiScope> GetAllApiResources()
{
return new List<ApiScope>
{
new ApiScope("TablesReachApi", "Api for solution")
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = { "TablesReachApi" }
}
};
}
}
Note: When I remove annotation [Authorize] from my api controller I can reach my method.
For some middleware, order matters. Authentication and authorization, for example, can't go in the order that you have put them in the API. Microsoft has some clear documentation on this for you to read here..

Asp Net Core, Logic for custom request headers

I'd like to create an Asp.Net Core MVC that can deny requests that lack specific headers.
For Example:
I want to access a Controller and and only allow Requests whose Header contains a specific (custom made) Authorization Type and Token.
I've done some research on it but I could not find anything on this topic that gave me an idea on how to even start.
You could custom a middleware to check the request header type and value:
public class SimpleHeaderAuthorizationMiddleware
{
private readonly RequestDelegate _next;
public SimpleHeaderAuthorizationMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
string authHeader = context.Request.Headers["Accept"];
if (!string.IsNullOrEmpty(authHeader))
{
if (authHeader == "specific_value")
{
//do your stuff....
//throw new Exception("The HTTP header value is not correct!");
context.Response.StatusCode = 403;
}
await _next(context);
}
else
{
//reject the request if do not provide Authorization header
//throw new Exception("Necessary HTTP header not present!");
context.Response.StatusCode = 401;
}
}
}
Create Middleware extension method:
public static class SimpleHeaderAuthorizationMiddlewareExtension
{
public static IApplicationBuilder UseSimpleHeaderAuthorization(this IApplicationBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
return app.UseMiddleware<SimpleHeaderAuthorizationMiddleware>();
}
}
The following code calls the middleware from Startup.Configure:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseSimpleHeaderAuthorization(); //add this...
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}

ASP.NET Core Odata Service Post not working

I implemented a ODATA Service in my ASP.NET Core application. The GET function is working fine, but I have some problems with the POST function.
If I excecute a POST the programm is excecuting the right method but I don't receive any data.
Is there anything missing in my code?
Controller:
[EnableCors]
[ODataRoutePrefix("documents")]
public class DocumentController : ODataController
{
[ODataRoute]
[EnableQuery]
public Document PushDocument([FromBody]Document doc)
{
System.Diagnostics.Debug.WriteLine("DomentID: " + doc.Id);
System.Diagnostics.Debug.WriteLine("Dokument: " + doc.RawDocument);
return doc;
}
}
Since you use [FromBody], you need to send data as Content-Type: application/json,in postman:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddOData();
services.AddMvc(options =>
{
options.EnableEndpointRouting = false;
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// 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(b =>
{
b.MapODataServiceRoute("odata", "odata", GetEdmModel());
});
}
private static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Document>("Documents");
builder.EntitySet<Press>("Presses");
return builder.GetEdmModel();
}

How to get route data from Identity Server 4 endpoints

I have a ResponseTimeMiddleware.cs responsible for getting response time metrics (I am using datadog) for every request made. Which is tagged by controller and action names. However when we hit the "connect/token" endpoint, the context.GetRouteData() is null, probably because identity server is doing it behind the scenes. Is there a way I could get this information or some other unique information where I could tag with?
here's my code:
public class ResponseTimeMiddleware
{
// other code..
public Task InvokeAsync(HttpContext context)
{
var request = context.Request;
var watch = new System.Diagnostics.Stopwatch();
watch.Start();
context.Response.OnStarting(() =>
{
watch.Stop();
var routeData = context.GetRouteData();
var responseTime = watch.ElapsedMilliseconds.ToString();
var tags = new[] { $"statusCode:{context.Response.StatusCode.ToString()}", $"controller:{routeData.Values["controller"]}", $"action:{routeData.Values["action"]}" };
context.Response.Headers[ResponseHeaderResponseTime] = responseTime;
DogStatsd.Timer("response.time", responseTime, tags: tags);
return Task.CompletedTask;
});
return nextDelegate(context);
}
}
This is my Startup:
public class Startup
{
// other code..
public static void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseMiddleware<ResponseTimeMiddleware>();
app.UseMvcWithDefaultRoute();
app.UseStaticFiles();
app.UseEndpointRouting();
app.UseCookiePolicy();
app.UseCors("CorsPolicy");
app.UseIdentityServer();
// This method gets called by the runtime. Use this method to add services to the container.
public async void ConfigureServices(IServiceCollection services)
{
services.AddDataDogStatsd(Configuration, "identity");
// other code
}
}
Use context.Request.Path conditionally if your routeData is null. It is the closest I can think of since Identity Server 4 middleware has internal routing logic for the standard OAuth protocol routes.

Razor pages shows empty page when changing Request.Path with a middleware

I'm using a middleware for subdomains. Middleware changes Request.Path as follows:
URL:
http://sub1.example.com
Request.Path:
/SubdomainPages/sub1
This works. But when URL contains a page (http://sub1.example.com/SomePage for instance), I'm getting a blank page. And when I use NotFound() action result in OnGet handler same thing occurs (I have /Pages/Errors/404.cshtml).
When I don't use middleware, pages and redirecting error page with NotFound() work well.
This is subdomain middleware:
public class SubdomainRoutingMiddleware
{
private readonly RequestDelegate _next;
public SubdomainRoutingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
string[] Subdomains = new string[] { "sub1", "sub2" };
var parts = context.Request.Host.Value.Split('.');
if (parts.Length > 2)
{
if (Subdomains.Contains(parts[0]))
{
if (context.Request.Path.Value != "/")
context.Request.QueryString = context.Request.QueryString.Add("sayfa", context.Request.Path.Value.Substring(1));
context.Request.Path = "/SubdomainPages/" + parts[0];
}
}
await _next.Invoke(context);
}
}
And this is Startup.cs file:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStatusCodePages();
app.UseStatusCodePagesWithReExecute("/Errors/{0}");
app.UseStaticFiles();
app.UseMiddleware<SubdomainRoutingMiddleware>();
app.UseMvc();
}
}
EDIT:
Request.Path changed with middleware when URL contains page, path becomes as follows:
URL:
http://sub1.example.com/SomePage
Request.Path:
/SubdomainPages/sub1/SomePage
/SomePage exists but /SubdomainPages/sub1/SomePage does not exist. This is a part of error.
I also added app.UseStatusCodePages() to Startup.cs. But still not working/calling 404 page when URL contains subdomain. And not showing status message like Status Code: 404; Not Found. Still showing blank page.
I found solution.
I also changed Request.Host in middleware. Now everything has been working as I expected.
public async Task Invoke(HttpContext context)
{
string[] Subdomains = new string[] { "sub1", "sub2" };
var parts = context.Request.Host.Value.Split('.');
if (parts.Length > 2)
{
if (Subdomains.Contains(parts[0]))
{
if (context.Request.Path.Value != "/")
context.Request.QueryString = context.Request.QueryString.Add("sayfa", context.Request.Path.Value.Substring(1));
context.Request.Path = "/SubdomainPages/" + parts[0];
// Change host (without subdomain)
context.Request.Host = new HostString("example.com");
}
}
await _next.Invoke(context);
}