0

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
2
  • How's the folder structure looking in your Views folder? Could you add that to your question? Commented Oct 24, 2020 at 7:54
  • Pio I've added the folder structure at the end of the question Commented Oct 24, 2020 at 8:52

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

7 Comments

Pio yes, but the problem is that route parameters are being treated as view names. how else are we supposed to pass route parameters?
SteinTheRuler I don't get your question? Do you want to pass the err route parameter to the Error.cshtml view?
Pio what I'm used to is controller name/action name/any additional data you want to pass to the view. what else is the point of the syntax {id?} in the default routing? it's an optional parameter and when defining the route you can define different parameters like user id or company name (examples)
Yeah and there is nothing wrong with your routing, just the way you wish to pass data to the view, if you read the documentation i posted: if you call View() with a string parameter it interpretes that as the name of the View you wish to show. If you want to pass stuff to the view, you need to pass it via model or viewbag. see my edit
Pio the problem is that it looks for a view named equal to the parameter value. so if I send /Home/Index/ErrorValue, it looks for a view called ErrorValue, which is strange, this has never been an issue before
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.