Razor Page page model file without corresponding content file

53 Views Asked by At

I'm using Razor Pages. Sometimes I need a page model file without the corresponding content file. But I must nonetheless specify a route for that page, and so define a content file anyway, just for the @page "/foo".

Example: the user clicks a link in a confirmation email, which leads to a token confirmation callback page /confirm. That redirects to various other pages based on whether the token was valid; e.g. /login, /resend-token, /error. So the page itself never renders content.

Confirm.cshtml

@page "/confirm"
@model ConfirmModel
@{ throw new InvalidOperationException("This page should never be rendered."); }

Confirm.cshtml.cs

public class ConfirmModel : PageModel
{
  public IActionResult OnGet()
  {
    // handles various scenarios; each redirects somewhere else
    // never returns `Page()`
    // ...
  }
}

Is there a way to avoid that useless Confirm.cshtml content file?

2

There are 2 best solutions below

4
Mike Brind On BEST ANSWER

If you need endpoints in a Razor Pages app that have no corresponding UI, you can use a standard MVC controller or a minimal API request handler (https://www.mikesdotnetting.com/article/358/using-minimal-apis-in-asp-net-core-razor-pages)

For example, in your Program.cs file, you would add this:

app.MapGet("/confirm", async (HttpContext context, [FromServices]IMyService service ) => {
    await service.PerformSomeTaskAsync();
    context.Response.Redirect("/some-url");
});
1
lonix On

Based on @MikeBrind's answer which explains that one can mix Razor Pages, MVC, WebAPI and Minimal APIs in the same project.

I opted for WebAPI as it's the simplest solution to my particular problem.

Update Program.cs:

services.AddRazorPages();
services.AddMvcCore();    // <--- (not strictly necessary, included by AddRazorPages)
// ...
app.MapRazorPages();
app.MapControllers();     // <---

Then add controllers, which can be located anywhere, not just in MyProject/Controllers/. To keep it simple, they can be placed alongside the RazorPages assets in MyProject/Pages/.

From my example above, convert this RazorPage duo /Pages/Confirm.cshtml.cs and /Pages/Confirm.cshtml:

public class ConfirmModel : PageModel
{
  public async Task<IActionResult> OnPostAsync() { /* ... */ }
}
@page "/confirm"
@model ConfirmModel

...to this WebAPI controller /Pages/Confirm.cs:

[ApiController]
[Route("[controller]")]
public class ConfirmController : ControllerBase {

  [HttpPost]
  public async Task<IActionResult> Post() { /* ... */ }

}

The nice thing about the WebAPI option is that controllers/actions are very similar to RazorPages in behaviour and have similar method signatures, which aids in "readability" and thus maintenance.

This approach also makes it really easy to add REST endpoints to the app (via ajax/fetch).