Use MVC, Razor Pages, Blazor, API Controllers, and Minimal APIs in a single
ASP.NET Core application

A few years ago I wrote
an article explaining how ASP.NET Core 2.0 MVC, Razor Pages, and Web API can
coexist in a single web application. ASP.NET Core has added some new members to
the family since then. And it's time to rebuild a similar web application that
uses ASP.NET Core 6.0 MVC, Razor Pages, Blazor Server, API controllers, and
minimal APIs together in a single project. Usually developers create a new
ASP.NET Core project based on a particular project template such as MVC or
Blazor. But you are not restricted to a single development option. ASP.NET Core
offers multiple development options and you can utilize them in a single project
depending on your requirement.
In this walkthrough I will start with an empty project and one-by-one add
MVC, Razor Pages, Blazor Server, API controller, and minimal API to it. This
will help you understand what configuration is necessary and how various files
are organized in such situations.
Let's get started by creating a new ASP.NET Core named AspNetCoreAllInOne
based on Empty project template.

The newly created project in the Solution Explorer is shown below.

Add ASP.NET Core MVC
First we will add MVC to the project. So, open Program.cs file and add this
code:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.MapDefaultControllerRoute();
app.Run();
Notice the code marked in bold letters. We call AddControllersWithViews() to
register MVC services with the DI container. The UseStaticFiles() call adds the
support foe static files (images, JS files etc.) to the app. The UseRouting()
and MapDefaultControllerRoute() methods handle the MVC routing used by the app.
In this example we are going to use the default /controller/action/id pattern
for the routes and hence we didn't specify any other route pattern.
Now add four folders namely Models, Views, Controllers, and wwwroot to the
project.
Then add a new class called AppModel to the Models folder using the Add New
Item dialog.

Write the following code in the AppModel.cs file:
namespace AspNetCoreAllInOne.Models
{
public class AppModel
{
public string Message { get; set; }
}
}
The AppModel class has just one property called Message.
Next, add a new controller class named HomeController to the Controllers
folder.

Write the following code in the HomeController class.
using Microsoft.AspNetCore.Mvc;
using AspNetCoreAllInOne.Models;
namespace AspNetCoreAllInOne.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
AppModel data = new AppModel()
{
Message = "Hello World!"
};
return View(data);
}
}
}
The HomeController class contains Index() action. The Index() action creates
an AppModel object, sets its Message property to Hello World! and passes that
model object to the Index view.
Create Views > Home folder and add a new Razor view file named Index.cshtml
in it.

Write the following code in the Index.cshtml file
@model AspNetCoreAllInOne.AppModel
<h1>@Model.Message</h1>
Here we simply output the Message property in the browser.
Add a new HTML page named Default.html in the wwwroot folder. We will use
this page later in this example. Just keep it empty for the time being.
At this stage your project will look like this:

If you run the application you will get this output:

Add ASP.NET Core Razor Pages
Now that MVC has been added to the project, let's go ahead and add Razor
Pages.
Open Program.cs file again and add this code:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.MapDefaultControllerRoute();
app.MapRazorPages();
app.Run();
Notice that we added two more calls - AddRazorPages() and MapRazorPages() -
for supporting Razor Pages.
Then add Pages folder under the project root. And add a new Razor Page named
Index.cshtml to it.

Open the Index.cshtml.cs page model class and add the following code:
public class IndexModel : PageModel
{
public AppModel Data = new AppModel();
public void OnGet()
{
Data.Message = "Hello Galaxy!";
}
}
We create an object of AppModel and set its Message property to Hello Galaxy!
in the OnGet() page handler.
Then open the Index.cshtml page and output the Message property as shown
below:
@page
@model AspNetCoreAllInOne.Pages.IndexModel
<h1>@Model.Data.Message</h1>
If you run the application you will see this output:

Note that after adding Razor Pages, the web application's default page got
changed from /Home/Index (MVC default) to /Index (Razor Pages default). Of
course, you can manually navigate to the desired URL and invoke MVC or Razor
Pages as required.
Add Blazor Server
Adding Blazor Server is slightly more complicated than adding MVC and Razor
Pages. Let's go ahead and do that.
Open the Program.cs file and add the following code:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.MapDefaultControllerRoute();
app.MapRazorPages();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
Notice the code marked in bold letters. We first call AddServerSideBlazor()
to register Blazor Server services. We then setup the Blazor and fallback
page using MapBlazorHub() and MapFallbackToPage() methods.
Next, add Blazor's root component - App.razor in the project's root folder.

Write the following code into App.razor.
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView>
<p>Sorry, there's
nothing at this address.</p>
<a href="/Default.html">Go to Default page</a>
</LayoutView>
</NotFound>
</Router>
Here we basically setup Blazor's router. Notice the anchor tag marked in bold
letters. If the router can't find a URL it will show an error message and will
allow the user to go to the Default.html page (we added Default.html page while
configuring MVC earlier in this example).
The App.razor component is loaded from the _Host.cshtml file. So, add a new
Razor View file named _Host.cshtml in the Pages folder.
Add the following code in the _Host.cshtml file.
@page
@using Microsoft.AspNetCore.Components.Web
@using AspNetCoreAllInOne
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,
initial-scale=1.0" />
<base href="~/" />
<component type="typeof(HeadOutlet)"
render-mode="ServerPrerendered" />
</head>
<body>
<component type="typeof(App)" render-mode="ServerPrerendered" />
<script src="_framework/blazor.server.js"></script>
</body>
</html>
Notice the code marked in bold letters. We render the App.razor component
using the <component> syntax. We also add a <script> reference to
blazor.server.js just before the </body> tag.
Next, we will add two simple Razor Components. First, create a new folder
called Shared under the project root. And add a new razor component named
WelcomeMessage.razor in it.
Write the following code inside WelcomeMessage component:
@code{
[Parameter]
public string Message { get; set; }
}
<h1>@Message</h1>
As you can see, there is a Message parameter property that will be used by a
parent component to specify a message. The specified message is simply outputted
on the page.
The WelcomeMessage.razor component is used by Index.razor component. So, add
Index.razor component in the Pages folder. You already added the Pages folder
while creating the Index Razor Page earlier in this example.
Write the following code in the Index.razor file.
@page "/"
@page "/IndexBlazor"
@using AspNetCoreAllInOne.Shared
<WelcomeMessage Message="Hello Universe!" />
We used the WelcomeMessage component and specify the Message to be Hello
Universe!. Notice that we have also used @page directive to specify a route /IndexBlazor.
This way the Index component can be accessed at /IndexBlazor. We already have
Index.cshtml file in the Pages folder. Therefore, we can't assign /Index route
to the newly added razor component.
These two components are shown in the Solution Explorer below:

Run the application and navigate to /IndexBlazor. You should get the
following output.

At this stage the Razor Pages default page is displayed in the browser. What
if we want to male our Blazor app as the default. Let's do that.
Rename Index.cshtml to IndexRazorPages.cshtml. And modify the _Host.cshtml as
shown below:
@page "/"
@using Microsoft.AspNetCore.Components.Web
@using AspNetCoreAllInOne
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,
initial-scale=1.0" />
<base href="~/" />
<component type="typeof(HeadOutlet)"
render-mode="ServerPrerendered" />
</head>
<body>
<component type="typeof(App)" render-mode="ServerPrerendered" />
<script src="_framework/blazor.server.js"></script>
</body>
</html>
Note that the @page directive now specifies the route to be /
If you run the app after making this change it will directly show the output
from Index.razor component.

Of course, you can also access the component using /IndexBlazor
Add API controller
Now let's add an API controller to the project. Open Program.cs again and add
the following lines of code:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddControllers();
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.MapDefaultControllerRoute();
app.MapRazorPages();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.MapControllers();
app.Run();
Notice the lines marked in bold letters. They are calls to AddControllers()
and MapControllers() and will take care of Web API related functionality.
Now add an API controller named ValuesController in the Controllers folder.

Write the following code in ValuesController.cs
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
string[] data = new string[] {
"Hello World!",
"Hello Galaxy!",
"Hello Universe!"
};
return Ok(data);
}
}
As an example we added just one action to the ValuesController. The Get()
action is marked with [HttpGet] attribute and simply returns an array of three
string values - Hello World!, Hello Galaxy!, and Hello Universe!
Run the application and navigate to /api/Values to invoke the Get() action.
Here is the output:

As expected the browser receives an array with three string elements.
Add minimal API
Adding minimal APIs is quite straightforward. Open Program.cs file and add
the following MapGet() call just before app.Run() :
app.MapGet("/minimalapi/values", () =>
{
string[] data = new string[] {
"Hello World!",
"Hello Galaxy!",
"Hello Universe!"
};
return Results.Ok(data);
});
As you can seem the MapGet() call specifies the endpoint URL to be /minimalapi/values
and the handler function simply returns an array of string values.
Run the app again and navigate to /minimalapi/values. Here is the output:

As you can see, the output is identical to the API controller but we invoked
the minimal API using a different endpoint (/minimalapi/values).
Using the Default.html page
We have added Default.html page in the wwwroot folder. Let's use it to
include URLs to all the endpoints we created so far.
Open Default.html and add the following code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<h1>This is default page!</h1>
<h3><a href="/Home/Index">
Go to ASP.NET Core MVC</a></h3>
<h3><a href="/IndexRazorPages">
Go to ASP.NET Core Razor Pages</a></h3>
<h3><a href="/IndexBlazor">
Go to ASP.NET Core Blazor</a></h3>
<h3><a href="/Api/Values">
Go to ASP.NET Core API Controller</a></h3>
<h3><a href="/MinimalApi/Values">
Go to ASP.NET Core Minimal API</a></h3>
</body>
</html>
This is a simple page containing hyperlinks and looks like this in the
browser:

Now open Index.razor component and remove this like:
@page "/"
The Index.razor won't be the default component anymore and you will get this
error after running the app.

Click on the Go to Default page link and you will go to the Default.html
page.
Click on each of the link and see if you get the correct output.
That's it for now! Keep coding!!