You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Razor Pages中InvalidOperationException多处理器匹配问题求助

问题描述

开发基于Razor Pages和C#的资产处置Web应用,采用LDAP验证用户账号密码,登录后将用户姓名传递到资产处置页面显示欢迎信息。用户登录后加载资产处置表单页面时触发以下错误:

An unhandled exception occurred while processing the request.
InvalidOperationException: Multiple handlers matched. The following handlers matched route data and had all constraints satisfied:

Void OnGet(), Void OnGet(System.String, System.String)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.DefaultPageHandlerMethodSelector.Select(PageContext context)

Stack Query Cookies Headers Routing
InvalidOperationException: Multiple handlers matched. The following handlers matched route data and had all constraints satisfied: Void OnGet(), Void OnGet(System.String, System.String)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.DefaultPageHandlerMethodSelector.Select(PageContext context)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeInnerFilterAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

相关代码如下:

登录页C#代码

using System.DirectoryServices.AccountManagement;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Surplus.Pages
{
    public class LoginModel : PageModel
    {
        [BindProperty]
        public string Username { get; set; }

        [BindProperty]
        public string Password { get; set; }

        public string ErrorMessage { get; set; }

        public IActionResult OnPost()
        {
            if (!ValidateUser(Username, Password))
            {
                ErrorMessage = "Invalid username or password.";
                return Page();
            }

            var user = GetUserDetailsFromLDAP(Username);
            if (user == null)
            {
                ErrorMessage = "Failed to retrieve user details from Server.";
                return Page();
            }

            // Pass user details to the surplus entry form
            return RedirectToPage("/SurplusEntryForm", new { firstName = user.FirstName, lastName = user.LastName });
        }

        private bool ValidateUser(string username, string password)
        {
            try
            {
                using (var context = new PrincipalContext(ContextType.Domain))
                {
                    // Validate the user's credentials against the LDAP server
                    return context.ValidateCredentials(username, password);
                }
            }
            catch (Exception ex)
            {
                // Handle any exception that occurred during the LDAP authentication
                // Log the exception or perform error handling as per your application's requirements
                Console.WriteLine($"LDAP authentication failed: {ex.Message}");
            }

            return false; // Return false if authentication fails or an error occurs
        }


        private User GetUserDetailsFromLDAP(string username)
        {
            try
            {
                using (var context = new PrincipalContext(ContextType.Domain))
                {
                    var userPrincipal = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username);

                    if (userPrincipal != null)
                    {
                        var firstName = userPrincipal.GivenName;
                        var lastName = userPrincipal.Surname;

                        // Create and return a User object
                        return new User { FirstName = firstName, LastName = lastName };
                    }
                }
            }
            catch (Exception ex)
            {
                // Handle any exception that occurred while retrieving user details from LDAP
                // Log the exception or perform error handling as per your application's requirements
                Console.WriteLine($"Failed to retrieve user details from Server: {ex.Message}");
            }

            return null; // Return null if the user is not found or an error occurs
        }

    }
}

User.cs模型代码

using System;
using System.Data.SqlClient;
using System.DirectoryServices;


namespace Surplus.Pages
{
    public class User
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string DatabaseConnectionString { get; set; }

        public bool ValidateLDAP(string userId, string password)
        {
            try
            {
                var ldapManager = new LdapManager();
                return ldapManager.Validate(userId, password);
            }
            catch (Exception ex)
            {
                // Handle any exception that occurred during LDAP validation
                Console.WriteLine($"LDAP validation failed: {ex.Message}");
            }

            return false;
        }

        public void ConnectToDatabase()
        {
            try
            {
                using (var connection = new SqlConnection(DatabaseConnectionString))
                {
                    connection.Open();

                    // Perform database operations here

                    connection.Close();
                }
            }
            catch (Exception ex)
            {
                // Handle any database connectivity exceptions
                Console.WriteLine($"Database connection failed: {ex.Message}");
            }
        }

        internal object GetUserDetailsFromLDAP(string username)
        {
            throw new NotImplementedException();
        }
    }

    public class LdapManager
    {
        public bool Validate(string userId, string password)
        {
            try
            {
                string path = LdapPath();
                string username = UserFullId(userId);
                DirectoryEntry de = new DirectoryEntry(path, username, password, AuthenticationTypes.Secure);
                DirectorySearcher ds = new DirectorySearcher(de);
                ds.FindOne();
                return true;
            }
            catch (DirectoryServicesCOMException)
            {
                return false;
            }
        }

        private string UserFullId(string userId)
        {
            string value = string.Format(@"{0}@{1}", userId, "YourDomainName");
            return value;
        }

        private string LdapPath()
        {
            string value = string.Format(@"LDAP://{0}:{1}", "YourDomainName", 389);
            return value;
        }
    }
}

资产处置页C#代码

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Collections.Generic;
using System.Data.SqlClient;

namespace Surplus.Pages
{
    public class SurplusEntryFormModel : PageModel
    {
        private readonly string _connectionString = "Data Source=dbname;Initial Catalog=tablename;User ID=dbusername;Password=dbpassword;";
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string WelcomeMessage { get; set; }
        [BindProperty]
        public string AssetTag { get; set; }
        [BindProperty]
        public string BulidingName { get; set; }
        [BindProperty]
        public string ItemName { get; set; }
        [BindProperty]
        public string Description { get; set; }
        [BindProperty]
        public string Reason { get; set; }

        public List<string> BuildingOptions { get; set; }

        public void OnGet()
        {
            // Initialize the BuildingOptions list
            BuildingOptions = new List<string>();

            try
            {
                using (var connection = new SqlConnection(_connectionString))
                {
                    connection.Open();

                    string query = "SELECT ID, BuildingName FROM SurplusItemsNew";
                    using (var command = new SqlCommand(query, connection))
                    {
                        using (var reader = command.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                string buildingName = reader["BuildingName"].ToString();
                                BuildingOptions.Add(buildingName);
                            }
                        }
                    }

                    connection.Close();
                }
            }
            catch (SqlException ex)
            {
                // Handle the exception
                // For simplicity, this example just rethrows the exception
                throw ex;
            }
        }


        public List<string> ReasonOptions { get; } = new List<string>
        {
            "Broken",
            "Burned Out",
            "Extinction",
            "No Longer Used",
            "Obsolete",
            "Other"
        };

        public void OnGet(string firstName, string lastName)
        {
            FirstName = firstName;
            LastName = lastName;
            WelcomeMessage = $"Welcome, {FirstName} {LastName}!";
        }

        public IActionResult OnPostSearchItem()
        {
            if (string.IsNullOrEmpty(AssetTag))
            {
                ModelState.AddModelError(string.Empty, "Please enter a valid Asset Tag.");
                return Page();
            }

            if (!ModelState.IsValid)
            {
                return Page();
            }

            try
            {
                using (var connection = new SqlConnection(_connectionString))
                {
                    connection.Open();

                    string query = "SELECT BulidingName, ItemName, Description, Reason FROM Items WHERE AssetTag = @assetTag";
                    using (var command = new SqlCommand(query, connection))
                    {
                        command.Parameters.AddWithValue("@assetTag", AssetTag);

                        using (var reader = command.ExecuteReader())
                        {
                            if (reader.Read())
                            {
                                BulidingName = reader["BulidingName"].ToString();
                                ItemName = reader["ItemName"].ToString();
                                Description = reader["Description"].ToString();
                                Reason = reader["Reason"].ToString();
                            }
                            else
                            {
                                ModelState.AddModelError(string.Empty, "Item not found with the given Asset Tag.");
                            }
                        }
                    }

                    connection.Close();
                }
            }
            catch (SqlException ex)
            {
                ModelState.AddModelError(string.Empty, $"Database connection failed: {ex.Message}");
            }

            return Page();
        }
    }
}
解决方案

错误原因

Razor Pages不允许同一个PageModel存在多个签名不同的OnGet方法。当页面被请求时,路由系统无法区分应该调用哪个OnGet,因此抛出Multiple handlers matched异常。你在SurplusEntryFormModel中定义了两个OnGet:无参数的和带firstName、lastName参数的,这就是问题根源。

修复步骤

将两个OnGet方法合并为一个,把参数设为可选(可为null),在同一个方法中处理初始化建筑选项和用户欢迎信息的逻辑:

public void OnGet(string firstName = null, string lastName = null)
{
    // 初始化建筑选项列表
    BuildingOptions = new List<string>();

    try
    {
        using (var connection = new SqlConnection(_connectionString))
        {
            connection.Open();

            string query = "SELECT ID, BuildingName FROM SurplusItemsNew";
            using (var command = new SqlCommand(query, connection))
            {
                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        string buildingName = reader["BuildingName"].ToString();
                        BuildingOptions.Add(buildingName);
                    }
                }
            }

            connection.Close();
        }
    }
    catch (SqlException ex)
    {
        throw ex;
    }

    // 处理用户欢迎信息
    if (!string.IsNullOrEmpty(firstName) && !string.IsNullOrEmpty(lastName))
    {
        FirstName = firstName;
        LastName = lastName;
        WelcomeMessage = $"Welcome, {FirstName} {LastName}!";
    }
}

这样修改后,无论是否传递firstName和lastName参数,都会调用同一个OnGet方法,路由系统可以正确匹配,解决异常问题。

额外优化建议

  • 用户信息传递方式优化:通过URL参数传递用户姓名存在安全风险(可被篡改),建议登录后将用户信息存储到HttpContext.Session中,在资产处置页面直接从Session读取:
    1. 在LoginModel的OnPost方法中添加:
      HttpContext.Session.SetString("FirstName", user.FirstName);
      HttpContext.Session.SetString("LastName", user.LastName);
      
    2. 在SurplusEntryFormModel的OnGet中读取:
      FirstName = HttpContext.Session.GetString("FirstName");
      LastName = HttpContext.Session.GetString("LastName");
      if (!string.IsNullOrEmpty(FirstName) && !string.IsNullOrEmpty(LastName))
      {
          WelcomeMessage = $"Welcome, {FirstName} {LastName}!";
      }
      
    3. 在Program.cs中启用Session:
      builder.Services.AddSession();
      // ...
      app.UseSession();
      
  • 异常处理优化:避免直接抛出数据库异常到页面,建议添加日志记录,并返回友好的错误提示。

内容的提问来源于stack exchange,提问作者djjailbreak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 02:27:07