Redirect to same page with an extra parameter

400 Views Asked by At

I am using Request Localization in a NET Core 7 and Razor Pages application:

builder.Services.AddRazorPages();

builder.Services.Configure<RequestLocalizationOptions>(options => {
  options.DefaultRequestCulture = new RequestCulture("pt");
  options.SupportedCultures = new List<CultureInfo> { new CultureInfo("en"), new CultureInfo("pt") };
  options.RequestCultureProviders.Insert(0, new RouteDataRequestCultureProvider { 
    RouteDataStringKey = "culture",
    UIRouteDataStringKey = "culture",
    Options = options
  }); 
});

WebApplication application = builder.Build();

application.UseRouting();
application.MapRazorPages();

application.UseRequestLocalization();

// ...

A few of my razor pages are:

Index: @page "/{culture?}"     Example: /pt

About: @page "/{culture?}/about"   Example: /pt/about

When the culture is null or invalid I want to redirect to same page with default culture.

I am trying to do this using a middleware:

public class RedirectUnsupportedCulturesMiddleware {

    private readonly RequestDelegate _next;
    private readonly string _routeDataStringKey;

    public RedirectUnsupportedCulturesMiddleware(
        RequestDelegate next,
        IOptions<RequestLocalizationOptions> monitor) {

        RequestLocalizationOptions options = monitor.Value;
        _next = next;
        var provider = options.RequestCultureProviders
            .Select(x => x as RouteDataRequestCultureProvider)
            .Where(x => x != null)
            .FirstOrDefault();
        _routeDataStringKey = provider.RouteDataStringKey;
    }

    public async Task Invoke(HttpContext context) {

        var requestedCulture = context.GetRouteValue(_routeDataStringKey)?.ToString();
        var cultureFeature = context.Features.Get<IRequestCultureFeature>();

        var actualCulture = cultureFeature?.RequestCulture.Culture.Name;

        if (string.IsNullOrEmpty(requestedCulture) ||
            !string.Equals(requestedCulture, actualCulture, StringComparison.OrdinalIgnoreCase)) {
            var newCulturedPath = GetNewPath(context, actualCulture);
            context.Response.Redirect(newCulturedPath);
            return;
        }

        await _next.Invoke(context);
    }

    private string GetNewPath(HttpContext context, string newCulture) {

        var routeData = context.GetRouteData();

        var router = routeData.Routers[0];
        var virtualPathContext = new VirtualPathContext(
            context,
            routeData.Values,
            new RouteValueDictionary { { _routeDataStringKey, newCulture } });

        return router.GetVirtualPath(virtualPathContext).VirtualPath;
    }
}

When I access the home page, e.g "/", I get an exception in the following:

var router = routeData.Routers[0];

Questions

  1. How to solve this?
  2. Can I use a 301 redirect from thee middleware?
  3. Can I use the Net Core UseRewriter to accomplish the same objective?
1

There are 1 best solutions below

3
On

Here is a whole working demo you could follow:

1.Custom IPageRouteModelConvention to map the route and then no need modify page route like @page "/{culture?}/xxx"

public class CustomCultureRouteRouteModelConvention : IPageRouteModelConvention
{
    public void Apply(PageRouteModel model)
    {
        List<SelectorModel> selectorModels = new List<SelectorModel>();
        foreach (var selector in model.Selectors.ToList())
        {
            var template = selector.AttributeRouteModel.Template;
            selectorModels.Add(new SelectorModel()
            {
                AttributeRouteModel = new AttributeRouteModel
                {
                    Template = "/{culture}" + "/" + template
                }
            });
        }
        foreach (var m in selectorModels)
        {
            model.Selectors.Add(m);
        }
    }
}

2.Add this page conventions in Program.cs

builder.Services.AddRazorPages().AddRazorPagesOptions(opts =>
{
    opts.Conventions.Add(new CustomCultureRouteRouteModelConvention());  //add this...
});
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
builder.Services.Configure<RequestLocalizationOptions>(
    opt =>
    {
       var supportCulteres = new List<CultureInfo>
       {
           new CultureInfo("pt"),
           new CultureInfo("fr")
       };
       opt.DefaultRequestCulture = new RequestCulture("pt");
       opt.SupportedCultures = supportCulteres;
       opt.SupportedUICultures = supportCulteres;
       opt.RequestCultureProviders.Insert(0, new RouteDataRequestCultureProvider()); //add this...
   });

3.The order of the middleware

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    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.UseRequestLocalization();
app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();
app.UseRequestLocalization();  //be sure add here......
app.UseAuthorization();

app.MapRazorPages();

app.Run();

Result:

enter image description here