SteinTech
SteinTech

Reputation: 4068

simple ASP.NET Core routing gone wrong

I'm having problems with routing, it looks like it thinks query parameters are view names, for some reason.

Config:

public static IServiceCollection AddAssetsSites(this IServiceCollection services, IConfiguration configuration, Action<MvcOptions> mvcOptions = default, params IAppFeatureBase[] features)
    {
        Identity ident = features.Exists(feat => feat.Type == AppFeatures.Identity) ? (Identity)features.Single(feat => feat.Type == AppFeatures.Identity) : new Identity(default, default);

        if (configuration.GetSection(AssetsStatics.ConfigSectionName).Exists())
        {
            IAssetsConfig cnf = new AssetsConfig(configuration);
            configuration.Bind(AssetsStatics.ConfigSectionName, cnf);

            services
                .AddOptions()
                .Configure<IConfiguration>(configuration)
                .AddTransient<IAssetsConfigAccessor, AssetsConfigAccessor>()
                .AddSingleton(cnf);
        }
        else
        {
            throw new ConfigSectionNotFoundException();
        }

        services
            .AddDbContext<AssetsDBContext>(opt => opt.UseSqlServer(AssetsStatics.ConnectionString))
            .AddTransient<IAssetsDBContext, AssetsDBContext>()
            .AddDbContext<AssetsIdentityDBContext>(opt => opt.UseSqlServer(AssetsStatics.ConnectionString))
            .AddTransient<IAssetsIdentityDBContext, AssetsIdentityDBContext>()
            .AddTransient<IAssetsDBContextAccessor, AssetsDBContextAccessor>()
            .AddHttpContextAccessor()
            .AddTransient<IActionContextAccessor, ActionContextAccessor>();

        services
            .AddTransient<IRepoFactory, RepoFactory>()
            .AddTransient<IServiceAccessFactory, ServiceAccessFactory>()
            .AddTransient<IQueryableExpressionFactory, QueryableExpressionFactory>()
            .AddTransient<IQueriesFactory, QueriesFactory>();

        services
            .AddIdentity<User, Role>(ident.IdentOptions)
            .AddUserManager<UserManager<User>>()
            .AddRoleManager<RoleManager<Role>>()
            .AddSignInManager<SignInManager<User>>()
            .AddEntityFrameworkStores<AssetsIdentityDBContext>()
            .AddDefaultTokenProviders().Services.ConfigureApplicationCookie(ident.CookieOptions)
            .AddTransient<IIdentityRepo, IdentityRepo>();

        if (features.Exists(feat => feat.Type == AppFeatures.SSL))
        {
            SSL ssl = (SSL)features.Single(feat => feat.Type == AppFeatures.SSL);

            services
                .AddHttpsRedirection(conf =>
                {
                    conf.HttpsPort = ssl.Port;
                });
        }

        services
            .AddAssetsRepos()
            .AddTransient<ITagHelperRepo, TagHelperRepo>()
            .AddTransient<ISitesHelper, SitesHelper>()
            .Configure<CookiePolicyOptions>(opt =>
            {
                opt.CheckConsentNeeded = context => true;
                opt.MinimumSameSitePolicy = SameSiteMode.Unspecified;
            })
            .AddSession(opt =>
            {
                opt.IdleTimeout = TimeSpan.FromMinutes(180);
            });


        if (features.Exists(cnf => cnf.Type == AppFeatures.Localization))
        {
            Localization local = (Localization)features.Single(cnf => cnf.Type == AppFeatures.Localization);

            services
                .AddControllersWithViews(mvcOptions)
                .AddDataAnnotationsLocalization()
                .AddViewLocalization(opt =>
                {
                    opt.ResourcesPath = local.ResourcePath;
                })
                .SetCompatibilityVersion(CoreStatics.DefaultCompatibilityVersion);
        }
        else
        {
            services
                .AddControllersWithViews(mvcOptions)
                .SetCompatibilityVersion(CoreStatics.DefaultCompatibilityVersion);
        }

        return services;
    }

    public static IApplicationBuilder UseAssetsSites(this IApplicationBuilder app, IConfiguration configuration, params IAppFeatureBase[] features)
    {
        if (features.Exists(feat => feat.Type == AppFeatures.Debug))
        {
            Debug dg = (Debug)features.Single(feat => feat.Type == AppFeatures.Debug);

            if (dg.Environment.IsDevelopment() || dg.IgnoreEnvironment)
            {
                app.UseDeveloperExceptionPage();
            }
        }

        if (features.Exists(feat => feat.Type == AppFeatures.SSL))
        {
            app.UseHttpsRedirection();
        }

        app
            .UseStaticFiles()
            .UseRouting()
            .UseSession()
            .UseCookiePolicy(new CookiePolicyOptions
            {
                CheckConsentNeeded = context => true,
                MinimumSameSitePolicy = SameSiteMode.None
            });

        if (features.Exists(feat => feat.Type == AppFeatures.Localization))
        {
            Localization local = (Localization)features.Single(feat => feat.Type == AppFeatures.Localization);

            app.UseRequestLocalization(opt =>
            {
                opt.DefaultRequestCulture = local.DefaultCulture;
                opt.SupportedCultures = local.SupportedCultures.ToList();
                opt.SupportedUICultures = local.SupportedCultures.ToList();
            });
        }

        if (features.Exists(feat => feat.Type == AppFeatures.DefaultRoute))
        {
            DefaultRoute route = (DefaultRoute)features.Single(feat => feat.Type == AppFeatures.DefaultRoute);

            app.UseEndpoints(opt =>
            {
                opt.MapControllerRoute("default", route.Route);
            });
        }
        else
        {
            app.UseEndpoints(opt => opt.MapDefaultControllerRoute());
        }

        return app;
    }
}

Controller:

public async Task<IActionResult> Index()
{
    return View();
}

[Route("[controller]/[action]/{err?}")]
public async Task<IActionResult> Error([FromRoute(Name = "err")] string err)
{
    return View(err);
}

Link:

url = Url.Action("Error", new { err = "missing" });

which generates: /Home/Error/creds

when the Error views loads I get:

InvalidOperationException: The view 'creds' was not found. The following locations were searched: /Views/Home/creds.en-US.cshtml /Views/Home/creds.en.cshtml /Views/Home/creds.cshtml /Views/Shared/creds.en-US.cshtml /Views/Shared/creds.en.cshtml /Views/Shared/creds.cshtml

Folder structure:

Views
    -Assets
        * Index.cshtml
    -Home
        * Error.cshtml
        * Index.cshtml
    -Shared
        * _Layout.cshtml
    -System
        * Index.cshtml
    * _ViewImports.cshtml
    * _ViewStart.cshtml

Upvotes: 0

Views: 508

Answers (1)

Pio
Pio

Reputation: 533

As suspected. Returning View(string viewname) searches your Views Folder with the pattern Views/ControllerName/ViewName.cshtml.

By adding the string parameter err You are telling it to find the creds.cshtml file in Views/Home/creds.cshtml (Assuming your Controllername is Home) (Hence the error message that states it doesnt exist).

If you wish to display the Error.cshtml a simple return View(); is enough because by default it will search for the *.cshtml file which matches the name of the action (i.e. Error.cshtml)

Some documentation: https://learn.microsoft.com/de-de/aspnet/core/mvc/views/overview?view=aspnetcore-3.1

EDIT

For passing the route parameter to the Error View you can either pass it via a model.

[Route("[controller]/[action]/{err?}")]
public async Task<IActionResult> Error([FromRoute(Name = "err")] string err)
{
    var errorModel = new ErrorModel(errorMessage: err);
    return View(errorModel);
}

Or a without a Model using the dynamic ViewBag

[Route("[controller]/[action]/{err?}")]
public async Task<IActionResult> Error([FromRoute(Name = "err")] string err)
{
    ViewBag.ErrorMessage = err;
    return View();
}

On the Error.cshtml you can then access the ViewBag.ErrorMessage and show it in div or something

Upvotes: 1

Related Questions