Search code examples
c#asp.net-corerazor-pages

Razor pages combined with ASP.NET Core MVC are having routing problems


I am new to Razor pages and to all ASP.NET Core, so most of the chances it is my ignorance...

I am developing an ASP.NET Core 7 web app. Initially I started from the MVC project template and then added Razor pages. The project is just at the beginning; it builds and runs, and presents me with a Swagger page:

enter image description here

The problem is that when I try to navigate to https://localhost:7229 or to http://localhost:5014, I get a http 404 "page not found" error.

Here is my Program.cs:

using SemanticTrainer.API.Controllers;
using AI.Config;
using EmbeddingsAzureOpenAI;
using EmbeddingsInterface;
using WordExtractorCore;
using Microsoft.ApplicationInsights.AspNetCore.Extensions;
using SemanticTrainer.API;
using SemanticTrainer.API.Pages;
using Microsoft.Extensions.DependencyInjection;
using TracerX.ExtensionMethods;
using DocumentFormat.OpenXml.Office2016.Drawing.ChartDrawing;
using Microsoft.Identity.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Configuration;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

// Configure logging 
builder.Configuration.AddXmlFile("config.xml", optional: false, reloadOnChange: true);
builder.Configuration.AddJsonFile("VectorProfile.json", optional: false, reloadOnChange: true);

// This comes from appsettings.json
var azureSettingsSection = builder.Configuration.GetSection("AzureSettings");
var azureSettings = ConfigurationValidator.ValidateAndGetAzureSettings(azureSettingsSection);
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();

var azConfig = builder.Configuration.Get<Config>();

if (azConfig == null)
    throw new InvalidOperationException("Config cannot be null");

// Register services with the dependency injection (DI) container
// using AddSingleton or AddTransient, where needed.

builder.Services.AddSingleton<AzureSettings>(azureSettings);
builder.Services.AddSingleton<IConfig>(azConfig);
builder.Services.AddSingleton<IEmbeddingsCreator>(provider => new AzureOpenAIEmbeddings(azConfig));
builder.Services.AddTransient<IExtractor,WordExtractor>();
builder.Services.AddSingleton<AzNamedEntities>();

// This is our custom class AzureClientFactory that builds AzureClient, AzureSearchIndex client, etc.
builder.Services.AddSingleton<IAzureClientFactory, AzureClientFactory>();
// Although the QueueProcessingService for Embeddings is registered as Singleton
// it is designed to process multiple paragraphs/segments and consider the call rate limiting.

// This inject automatically creates Logger and AzureOpenAIEmbeddings objects and injects them

// to newly created QueueProcessingService
builder.Services.AddSingleton<QueueProcessingService>(provider =>
{
    var logger = provider.GetRequiredService<ILogger<QueueProcessingService>>();
    var azureOpenAIEmbeddings = provider.GetRequiredService<IEmbeddingsCreator>();
    var azNamedEntities = provider.GetRequiredService<AzNamedEntities>();
    return new QueueProcessingService(logger, DocController.mSegmentQueue, azureOpenAIEmbeddings, azNamedEntities);
}); 

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddControllersWithViews(options =>
{
    var policy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
    options.Filters.Add(new AuthorizeFilter(policy));
}); //.AddRazorPagesOptions();

builder.Services.AddRazorPages();

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddSwaggerGen();
builder.Services.AddSignalR();
builder.Services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        options.Conventions.AddPageRoute("/", "Index");
        options.RootDirectory = "/Pages";
    });

// Configure authentication using Microsoft Identity Platform,
// specifically Azure AD as part of it.
// Sign-in users of the same organization
// https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/blob/master/1-WebApp-OIDC/1-1-MyOrg/Startup.cs
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
       .AddMicrosoftIdentityWebApp(options => builder.Configuration.Bind("AzureAd", options));

if (builder.Environment.IsProduction() || builder.Environment.IsStaging())
{
    // For non-dev environment
    ApplicationInsightsServiceOptions? insightOptions = builder.Configuration.GetSection("ApplicationInsights").Get<ApplicationInsightsServiceOptions>();

    if (insightOptions == null || string.IsNullOrWhiteSpace(insightOptions.ConnectionString))
    {
        throw new InvalidOperationException("Application Insights connection string is not configured properly.");
    }
    builder.Services.AddApplicationInsightsTelemetry(insightOptions);
}

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
else
{
    app.UseHttpsRedirection();
}

// This will be implemented as Attribute-based routing inside controllers
app.UseRouting();

// Configure authentication globally for the application
app.UseAuthentication();
// Configure authorization policies for the application
app.UseAuthorization();

// This should provide Razor Pages routing
app.MapControllers();
app.MapHub<NotificationHub>("/notificationhub");

// Instantiate the service:
// Ensure this service starts immediately so it is ready to process when segments become available
var queueProcessingService = app.Services.GetRequiredService<QueueProcessingService>();
var azNamedEntities = app.Services.GetRequiredService<AzNamedEntities>();
azNamedEntities.Init();

//app.MapRazorPages();
app.UseSwagger();
app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/sawgger/v1/swagger.json", "API v1");
    });

app.Run();

My Index.cshtml and Index.cshtml.cs are in the Pages folder and look like this:

@page
@model SemanticTrainer.API.Pages.IndexModel
@{
    ViewData["Title"] = "Home";
}

<div class="container">
    <h1>Welcome to Your Application</h1>
    <p>This is the home page of your application.</p>

    @if (User?.Identities != null)
    {
        if (User?.Identity?.IsAuthenticated == false)
        {
            <p><a class="btn btn-primary" href="/AzureAD/Login">Login with Azure AD</a></p>
        }
        else
        {
            <p>You are logged in as: @User?.Identity?.Name</p>
            <!-- Add additional content for authenticated users here -->
        }
    }
</div>

------------------------

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace SemanticTrainer.API.Pages
{
    public class LoginModel : PageModel
    {
        public IActionResult OnPost()
        {
            // Trigger Azure AD/OIDC authentication
            return Challenge(new AuthenticationProperties { RedirectUri = "/" });
        }
    }
}

The bottom line is that I ran out of options to try. It is very well might be because I mixed MVC with Razor pages, which all the documents say that it is ok to do. As far as I understand the Razor pages intercept all calls and decide whether to route to MVC or handle them by themselves. Anyway, I am desperately looking for solution.

Now, if all you have to say get rid of MVC and try without it I already have it in my mind as a last resort... I hope for a more helpful answer... Thank you in advance.


Solution

  • I resolved the problem and now it authenticates against Azure AD and displays the home page. The problem was not in the application but in Visual Studio. Apparently ASP.NET Core continues to be in a fluid state. I updated VS2022 to the latest version 17.8.1 and now all projects from GitHub that involve Razor Pages, or those built from Project Templates work "as is".