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

ASP.NET MVC 5表单提交传递路由参数至控制器方法的问题

问题:ASP.NET MVC5表单提交时传递租户ID路由参数失败

背景

基于.NET 4.8构建ASP.NET MVC5多租户应用,配置SSO时需要在整个登录流程URL中保留当前租户ID,通过表单提交(需保留防伪令牌)向控制器POST方法传递该路由参数,但目前无法正常运行。

现有表单代码

@using (Html.BeginForm("ExternalLogin/" + Request.Url.Segments[Request.Url.Segments.Length - 1], "AccountSSO", new { ReturnUrl = "" }))
{
    @Html.AntiForgeryToken()
    <div id="socialLoginList">
        <p>
            <button type="submit" class="btn btn-default" name="provider" value="OpenIdConect" title="SSO Login">SSO Login</button>
        </p>
    </div>
}

注:租户ID作为路由参数,通过Request.Url.Segments获取并拼接在Action名称后

控制器Action代码

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(int tenantId)
{    
    // 请求重定向到外部登录提供商
    var res = new ChallengeResult("OpenIdConnect", Url.Action($"ExternalLoginCallback/{tenantId}", "AccountSSO"));
    return res;
}

报错信息

The parameters dictionary contains a null entry for parameter 'tenantId' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult ExternalLogin(Int32)' in 'MyProject.Controllers.AccountSSOController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter. Parameter name: parameters

路由配置(Global.asax最后三条)

// default route
var defaults = new RouteValueDictionary();
routes.Add("Default", new Route("api/{controller}/{action}", defaults, new RouteDefaultHandler()));

// default route with param
var defaultsWithParam = new RouteValueDictionary();
routes.Add("DefaultWithParam", new Route("api/{controller}/{action}/{param}", defaultsWithParam, new RouteDefaultHandler()));

//catch-all route
var notFoundDefaults = new RouteValueDictionary();
notFoundDefaults.Add("controller", "DefaultPage");
notFoundDefaults.Add("action", "Show");
routes.Add("DefaultPage", new Route("{*url}", notFoundDefaults, new MvcRouteHandler()));

解决方案

  • 方法1:修正路由定义,匹配tenantId参数
    当前带参数的路由使用{param},但控制器Action参数名为tenantId,名称不匹配导致无法绑定。修改路由配置,将参数名改为tenantId:
// 修改带参数的路由
var defaultsWithParam = new RouteValueDictionary();
// 把{param}改为{tenantId},确保与Action参数名一致
routes.Add("DefaultWithParam", new Route("api/{controller}/{action}/{tenantId}", defaultsWithParam, new RouteDefaultHandler()));

然后调整表单的BeginForm调用,使用路由值而非硬拼接URL:

@{
    var tenantId = int.Parse(Request.Url.Segments[Request.Url.Segments.Length - 1]);
}
@using (Html.BeginForm("ExternalLogin", "AccountSSO", new { tenantId = tenantId, ReturnUrl = "" }, FormMethod.Post))
{
    @Html.AntiForgeryToken()
    <div id="socialLoginList">
        <p>
            <button type="submit" class="btn btn-default" name="provider" value="OpenIdConnect" title="SSO Login">SSO Login</button>
        </p>
    </div>
}
  • 方法2:通过隐藏表单字段传递参数
    如果不想修改路由,可以在表单中添加隐藏字段,将tenantId作为POST表单数据传递:
@{
    var tenantId = Request.Url.Segments[Request.Url.Segments.Length - 1];
}
@using (Html.BeginForm("ExternalLogin", "AccountSSO", new { ReturnUrl = "" }))
{
    @Html.AntiForgeryToken()
    <!-- 添加隐藏字段传递tenantId -->
    <input type="hidden" name="tenantId" value="@tenantId" />
    <div id="socialLoginList">
        <p>
            <button type="submit" class="btn btn-default" name="provider" value="OpenIdConnect" title="SSO Login">SSO Login</button>
        </p>
    </div>
}

这种方式不需要修改路由,MVC会自动将表单中的tenantId字段绑定到Action的同名参数上。

  • 方法3:从Request URL中手动提取参数
    如果上述方法都不适用,可以在Action中直接从请求URL的Segments中提取租户ID:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin()
{    
    var tenantId = int.Parse(Request.Url.Segments[Request.Url.Segments.Length - 1]);
    var res = new ChallengeResult("OpenIdConnect", Url.Action($"ExternalLoginCallback/{tenantId}", "AccountSSO"));
    return res;
}

这种方式不需要依赖路由绑定,直接手动获取参数,但要注意添加异常处理(比如解析失败的情况)。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 00:45:36