Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return a different Razor Page without redirect?

Tags:

razor-pages

I am porting a Asp.Net MVC application to Razor Pages.

In some of the controllers of the MVC application it makes use of return View("someOtherView", someModelForOtherView);

How do I port this to Razor Pages? What I need to do is to transfer the request over to another Razor Page and pass the prepared PageModel to it (the other page does not need to execute OnMethod() but simply render its html. Or, in other words, I only need to swap the template file that should be rendered with another one.

I cannot use Redirect as there must not be another roundtrip via the browser.

like image 806
Sebastian P.R. Gingter Avatar asked May 24 '18 12:05

Sebastian P.R. Gingter


1 Answers

I doubt this is (easily) possible. From the github request that Lerner linked above, it's noted Razor Pages weren't designed to do that.

The closest workaround I was able to achieve was to turn my destination Razor Page into a View. (Hence, no code-behind.) Obviously that will only be possible if your destination page is never directly accessed via URL. For example, if you want to redirect to /Pages/MyPage, and you still need to be able to access the url http://example.com/MyPage, this won't work.

But, say all you want is a generic error or status page. Those don't have to be directly-accessible through URL. This works well for that.

Here's a couple extension methods on PageModel to do it, one that accepts models and one that doesn't:

public static ViewResult View(this PageModel pageModel, string viewName) {
    return new ViewResult() {
        ViewName = viewName,
        ViewData = pageModel.ViewData,
        TempData = pageModel.TempData
    };
}

public static ViewResult View<TModel>(this PageModel pageModel, string viewName, TModel model) {
    var viewDataDictionary = new ViewDataDictionary<TModel>(new EmptyModelMetadataProvider(), new ModelStateDictionary()) {
        Model = model
    };
    foreach (var kvp in pageModel.ViewData) viewDataDictionary.Add(kvp);

    return new ViewResult {
        ViewName = viewName,
        ViewData = viewDataDictionary,
        TempData = pageModel.TempData
    };
}

FYI, the reason for having to recreate the view dictionary is because the one in your pageModel is going to have a model type specific to the current Page, not to the View you're directing to, and you can't change the Model within a ViewDataDictionary to a different type. MVC would complain and throw an exception.

Usage:

public IActionResult OnGet(string id) {
    // check if id is good here     
    if (idIsNoGood) return this.View("InvalidId", new ErrorModel...);
    else {
        return Page();
    }
}

The above will look for InvalidId.cshtml view, which can be in the same folder as your page, the root /Pages/ folder, or /Pages/Shared/. And it'll still use your Layout too, like any other page.

Just make sure your cshtml file doesn't have a @page directive at the top; this won't work for a Razor page, only a View.

Example InvalidId.cshtml:

@model MyProject.Models.ErrorModel
<h1>Invalid Request</h1>
<p>@Model.Message</p>
like image 159
Ber'Zophus Avatar answered Nov 07 '22 19:11

Ber'Zophus