我尝试这样做的主要动机是获取仅在页面底部的部分与其余 Javascript 而不是在呈现部分的页面中间需要的 Javascript。
这是我正在尝试做的一个简化示例:
这是在正文之前带有脚本部分的布局。
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
</head>
<body>
@RenderBody()
<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
@RenderSection("Scripts", false)
</body>
</html>
这是使用此布局的示例视图。
<h2>This is the view</h2>
@{Html.RenderPartial("_Partial");}
@section Scripts {
<script type="text/javascript">
alert("I'm a view.");
</script>
}
这是从视图中渲染的部分。
<p>This is the partial.</p>
@* this never makes it into the rendered page *@
@section Scripts {
<script type="text/javascript">
alert("I'm a partial.");
</script>
}
在此示例中,视图中指定的标记被放置在部分中,但部分中的标记不是。是否可以使用 Razor 从局部视图中填充部分?如果没有,还有哪些其他方法可以获取仅页面底部的部分需要而不在全局范围内包含的 Javascript?
我处理这个问题的方法是为 HtmlHelper 类编写几个扩展方法。这允许局部视图说它们需要一个脚本,然后在布局视图中写入我调用到我的辅助方法的标记以发出所需的脚本
以下是辅助方法:
public static string RequireScript(this HtmlHelper html, string path, int priority = 1)
{
var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as List<ResourceInclude>;
if (requiredScripts == null) HttpContext.Current.Items["RequiredScripts"] = requiredScripts = new List<ResourceInclude>();
if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceInclude() { Path = path, Priority = priority });
return null;
}
public static HtmlString EmitRequiredScripts(this HtmlHelper html)
{
var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as List<ResourceInclude>;
if (requiredScripts == null) return null;
StringBuilder sb = new StringBuilder();
foreach (var item in requiredScripts.OrderByDescending(i => i.Priority))
{
sb.AppendFormat("<script src=\"{0}\" type=\"text/javascript\"></script>\n", item.Path);
}
return new HtmlString(sb.ToString());
}
public class ResourceInclude
{
public string Path { get; set; }
public int Priority { get; set; }
}
一旦你有了它,你的局部视图只需要调用 @Html.RequireScript("/Path/To/Script")
。
在布局视图的 head 部分中,您调用 @Html.EmitRequiredScripts()
。
另一个好处是它允许您清除重复的脚本请求。如果您有多个需要给定脚本的视图/部分视图,您可以放心地假设您只会输出一次
部分视图不能参与其父视图的部分。
你可以有第二个部分,它只负责注入必要的 javascript。如果需要,可以在 @if
块周围放置几个脚本:
@model string
@if(Model == "bla") {
<script type="text/javascript">...</script>
}
@else if(Model == "bli") {
<script type="text/javascript">...</script>
}
这显然可以稍微清理一下,但是,在您视图的 Scripts
部分:
@section Scripts
{
@Html.Partial("_Scripts", "ScriptName_For_Partial1")
}
同样,它可能不会赢得美容奖,但它会起作用。
更优雅的方法是将部分视图脚本移动到单独的文件中,然后在视图的脚本部分中呈现它:
<h2>This is the view</h2>
@Html.RenderPartial("_Partial")
@section Scripts
{
@Html.RenderPartial("_PartialScripts")
<script type="text/javascript">
alert("I'm a view script.");
</script>
}
局部视图_Partial.cshtml:
<p>This is the partial.</p>
仅包含脚本的部分视图 _PartialScripts.cshtml:
<script type="text/javascript">
alert("I'm a partial script!");
</script>
安装 Forloop.HtmlHelpers nuget 包 - 它添加了一些帮助程序来管理部分视图和编辑器模板中的脚本。
在您的布局中的某处,您需要调用
@Html.RenderScripts()
这将是页面中输出任何脚本文件和脚本块的位置,因此我建议将其放在布局中的主要脚本之后和脚本部分(如果有的话)之后。
如果您将 Web 优化框架与捆绑一起使用,则可以使用重载
@Html.RenderScripts(Scripts.Render)
以便此方法用于写出脚本文件。
现在,只要您想在视图、局部视图或模板中添加脚本文件或块,只需使用
@using (Html.BeginScriptContext())
{
Html.AddScriptFile("~/Scripts/jquery.validate.js");
Html.AddScriptBlock(
@<script type="text/javascript">
$(function() { $('#someField').datepicker(); });
</script>
);
}
如果多次添加,帮助程序确保只呈现一个脚本文件引用,并且它还确保脚本文件以预期的顺序呈现,即
布局部分和模板(按照它们在视图中出现的顺序,从上到下)
[更新版本] 在@Necrocubus 问题之后更新版本以包含内联脚本。
public static class ScriptsExtensions
{
const string REQ_SCRIPT = "RequiredScript";
const string REQ_INLINESCRIPT = "RequiredInlineScript";
const string REQ_STYLE = "RequiredStyle";
#region Scripts
/// <summary>
/// Adds a script
/// </summary>
/// <param name="html"></param>
/// <param name="path"></param>
/// <param name="priority">Ordered by decreasing priority </param>
/// <param name="bottom"></param>
/// <param name="options"></param>
/// <returns></returns>
public static string RequireScript(this IHtmlHelper html, string path, int priority = 1, bool bottom=false, params string[] options)
{
var ctxt = html.ViewContext.HttpContext;
var requiredScripts = ctxt.Items[REQ_SCRIPT] as List<ResourceToInclude>;
if (requiredScripts == null) ctxt.Items[REQ_SCRIPT] = requiredScripts = new List<ResourceToInclude>();
if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceToInclude() { Path = path, Priority = priority, Options = options, Type=ResourceType.Script, Bottom=bottom});
return null;
}
/// <summary>
///
/// </summary>
/// <param name="html"></param>
/// <param name="script"></param>
/// <param name="priority">Ordered by decreasing priority </param>
/// <param name="bottom"></param>
/// <returns></returns>
public static string RequireInlineScript(this IHtmlHelper html, string script, int priority = 1, bool bottom = false)
{
var ctxt = html.ViewContext.HttpContext;
var requiredScripts = ctxt.Items[REQ_INLINESCRIPT] as List<InlineResource>;
if (requiredScripts == null) ctxt.Items[REQ_INLINESCRIPT] = requiredScripts = new List<InlineResource>();
requiredScripts.Add(new InlineResource() { Content=script, Priority = priority, Bottom=bottom, Type=ResourceType.Script});
return null;
}
/// <summary>
/// Just call @Html.EmitRequiredScripts(false)
/// at the end of your head tag and
/// @Html.EmitRequiredScripts(true) at the end of the body if some scripts are set to be at the bottom.
/// </summary>
public static HtmlString EmitRequiredScripts(this IHtmlHelper html, bool bottom)
{
var ctxt = html.ViewContext.HttpContext;
var requiredScripts = ctxt.Items[REQ_SCRIPT] as List<ResourceToInclude>;
var requiredInlineScripts = ctxt.Items[REQ_INLINESCRIPT] as List<InlineResource>;
var scripts = new List<Resource>();
scripts.AddRange(requiredScripts ?? new List<ResourceToInclude>());
scripts.AddRange(requiredInlineScripts ?? new List<InlineResource>());
if (scripts.Count==0) return null;
StringBuilder sb = new StringBuilder();
foreach (var item in scripts.Where(s=>s.Bottom==bottom).OrderByDescending(i => i.Priority))
{
sb.Append(item.ToString());
}
return new HtmlString(sb.ToString());
}
#endregion Scripts
#region Styles
/// <summary>
///
/// </summary>
/// <param name="html"></param>
/// <param name="path"></param>
/// <param name="priority">Ordered by decreasing priority </param>
/// <param name="options"></param>
/// <returns></returns>
public static string RequireStyle(this IHtmlHelper html, string path, int priority = 1, params string[] options)
{
var ctxt = html.ViewContext.HttpContext;
var requiredScripts = ctxt.Items[REQ_STYLE] as List<ResourceToInclude>;
if (requiredScripts == null) ctxt.Items[REQ_STYLE] = requiredScripts = new List<ResourceToInclude>();
if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceToInclude() { Path = path, Priority = priority, Options = options });
return null;
}
/// <summary>
/// Just call @Html.EmitRequiredStyles()
/// at the end of your head tag
/// </summary>
public static HtmlString EmitRequiredStyles(this IHtmlHelper html)
{
var ctxt = html.ViewContext.HttpContext;
var requiredScripts = ctxt.Items[REQ_STYLE] as List<ResourceToInclude>;
if (requiredScripts == null) return null;
StringBuilder sb = new StringBuilder();
foreach (var item in requiredScripts.OrderByDescending(i => i.Priority))
{
sb.Append(item.ToString());
}
return new HtmlString(sb.ToString());
}
#endregion Styles
#region Models
public class InlineResource : Resource
{
public string Content { get; set; }
public override string ToString()
{
return "<script>"+Content+"</script>";
}
}
public class ResourceToInclude : Resource
{
public string Path { get; set; }
public string[] Options { get; set; }
public override string ToString()
{
switch(Type)
{
case ResourceType.CSS:
if (Options == null || Options.Length == 0)
return String.Format("<link rel=\"stylesheet\" href=\"{0}\" type=\"text/css\" />\n", Path);
else
return String.Format("<link rel=\"stylesheet\" href=\"{0}\" type=\"text/css\" {1} />\n", Path, String.Join(" ", Options));
default:
case ResourceType.Script:
if (Options == null || Options.Length == 0)
return String.Format("<script src=\"{0}\" type=\"text/javascript\"></script>\n", Path);
else
return String.Format("<script src=\"{0}\" type=\"text/javascript\" {1}></script>\n", Path, String.Join(" ", Options));
}
}
}
public class Resource
{
public ResourceType Type { get; set; }
public int Priority { get; set; }
public bool Bottom { get; set; }
}
public enum ResourceType
{
Script,
CSS
}
#endregion Models
}
我的 2 美分,这是一个旧帖子,但仍然相关,所以这里是贝尔先生解决方案的升级更新,它适用于 ASP.Net Core。
它允许从导入的部分视图和子视图向主布局添加脚本和样式,并可以向脚本/样式导入添加选项(如异步延迟等):
public static class ScriptsExtensions
{
const string REQ_SCRIPT = "RequiredScript";
const string REQ_STYLE = "RequiredStyle";
public static string RequireScript(this IHtmlHelper html, string path, int priority = 1, params string[] options)
{
var ctxt = html.ViewContext.HttpContext;
var requiredScripts = ctxt.Items[REQ_SCRIPT] as List<ResourceInclude>;
if (requiredScripts == null) ctxt.Items[REQ_SCRIPT] = requiredScripts = new List<ResourceInclude>();
if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceInclude() { Path = path, Priority = priority, Options = options });
return null;
}
public static HtmlString EmitRequiredScripts(this IHtmlHelper html)
{
var ctxt = html.ViewContext.HttpContext;
var requiredScripts = ctxt.Items[REQ_SCRIPT] as List<ResourceInclude>;
if (requiredScripts == null) return null;
StringBuilder sb = new StringBuilder();
foreach (var item in requiredScripts.OrderByDescending(i => i.Priority))
{
if (item.Options == null || item.Options.Length == 0)
sb.AppendFormat("<script src=\"{0}\" type=\"text/javascript\"></script>\n", item.Path);
else
sb.AppendFormat("<script src=\"{0}\" type=\"text/javascript\" {1}></script>\n", item.Path, String.Join(" ", item.Options));
}
return new HtmlString(sb.ToString());
}
public static string RequireStyle(this IHtmlHelper html, string path, int priority = 1, params string[] options)
{
var ctxt = html.ViewContext.HttpContext;
var requiredScripts = ctxt.Items[REQ_STYLE] as List<ResourceInclude>;
if (requiredScripts == null) ctxt.Items[REQ_STYLE] = requiredScripts = new List<ResourceInclude>();
if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceInclude() { Path = path, Priority = priority, Options = options });
return null;
}
public static HtmlString EmitRequiredStyles(this IHtmlHelper html)
{
var ctxt = html.ViewContext.HttpContext;
var requiredScripts = ctxt.Items[REQ_STYLE] as List<ResourceInclude>;
if (requiredScripts == null) return null;
StringBuilder sb = new StringBuilder();
foreach (var item in requiredScripts.OrderByDescending(i => i.Priority))
{
if (item.Options == null || item.Options.Length == 0)
sb.AppendFormat("<link rel=\"stylesheet\" href=\"{0}\" type=\"text/css\" />\n", item.Path);
else
sb.AppendFormat("<link rel=\"stylesheet\" href=\"{0}\" type=\"text/css\" {1} />\n", item.Path, String.Join(" ", item.Options));
}
return new HtmlString(sb.ToString());
}
public class ResourceInclude
{
public string Path { get; set; }
public int Priority { get; set; }
public string[] Options { get; set; }
}
}
<text>
,只需将其添加为字符串(如果您愿意,您仍然可以为多行添加 @"" 前缀),并且没有 <script>
标记
对于那些寻找 aspnet core 2.0 版本的人:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Http;
public static class HttpContextAccessorExtensions
{
public static string RequireScript(this IHttpContextAccessor htmlContextAccessor, string path, int priority = 1)
{
var requiredScripts = htmlContextAccessor.HttpContext.Items["RequiredScripts"] as List<ResourceInclude>;
if (requiredScripts == null) htmlContextAccessor.HttpContext.Items["RequiredScripts"] = requiredScripts = new List<ResourceInclude>();
if (requiredScripts.All(i => i.Path != path)) requiredScripts.Add(new ResourceInclude() { Path = path, Priority = priority });
return null;
}
public static HtmlString EmitRequiredScripts(this IHttpContextAccessor htmlContextAccessor)
{
var requiredScripts = htmlContextAccessor.HttpContext.Items["RequiredScripts"] as List<ResourceInclude>;
if (requiredScripts == null) return null;
StringBuilder sb = new StringBuilder();
foreach (var item in requiredScripts.OrderByDescending(i => i.Priority))
{
sb.AppendFormat("<script src=\"{0}\" type=\"text/javascript\"></script>\n", item.Path);
}
return new HtmlString(sb.ToString());
}
public class ResourceInclude
{
public string Path { get; set; }
public int Priority { get; set; }
}
}
在脚本渲染部分调用后添加到您的布局:
@HttpContextAccessor.EmitRequiredScripts()
在你的部分观点中:
@inject IHttpContextAccessor HttpContextAccessor
...
@HttpContextAccessor.RequireScript("/scripts/moment.min.js")
您可以创建一个新的 Layout
页面并将 PartialView 包装在负责呈现内容以及任何库部分的完整视图中。
例如,假设我有以下代码:
HomeController.cs
[HttpGet]
public ActionResult About()
{
var vm = new AboutViewModel();
return View("About", vm);
}
呈现整页视图时,通常通过合并两个文件来呈现:
About.cshtml
@model AboutViewModel
@{
ViewBag.Title = "About CSHN";
}
<h3>@ViewBag.Title</h3>
@section Styles {
<style> /* style info here */ </style>
}
@section Scripts {
<script> /* script info here */ </script>
}
_Layout.cshtml
(或在 _ViewStart 中指定或在页面中覆盖的任何内容)
<!DOCTYPE html>
<html>
<head>
@RenderSection("Styles", false)
<title>@ViewBag.Title</title>
</head>
<body>
@RenderBody()
@RenderSection("scripts", false)
</body>
</html>
现在,假设您想将 About.cshtml
渲染为 局部视图,或许作为响应 AJAX 调用的模式窗口。此处的目标是仅返回关于页面、脚本和所有内容中指定的内容,而不包含 _Layout.cshtml
主布局中包含的所有膨胀(如完整的 <html>
文档)。
您可以这样尝试,但它不会附带任何部分块:
return PartialView("About", vm);
相反,添加一个更简单的布局页面,如下所示:
_PartialLayout.cshtml
<div>
@RenderBody()
@RenderSection("Styles", false)
@RenderSection("scripts", false)
</div>
或者支持这样的模式窗口:
_ModalLayout.cshtml
<div class="modal modal-page fade" tabindex="-1" role="dialog" >
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title">@ViewBag.Title</h4>
</div>
<div class="modal-body">
@RenderBody()
@RenderSection("Styles", false)
@RenderSection("scripts", false)
</div>
<div class="modal-footer">
<button type="button" class="btn btn-inverse" data-dismiss="modal">Dismiss</button>
</div>
</div>
</div>
</div>
然后您可以在此控制器或任何其他要同时呈现视图的内容和脚本的处理程序中specify a custom Master View
[HttpGet]
public ActionResult About()
{
var vm = new AboutViewModel();
return !Request.IsAjaxRequest()
? View("About", vm)
: View("About", "~/Views/Shared/_ModalLayout.cshtml", vm);
}
根据上面 Bell And Shimmy 先生的回答,我为 Bundle 脚本添加了额外的功能。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.Web.Mvc;
namespace ABC.Utility
{
public static class PartialViewHelper
{
public static string RequireScript(this HtmlHelper html, string path, int priority = 1)
{
var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as List<ResourceInclude>;
if (requiredScripts == null) HttpContext.Current.Items["RequiredScripts"] = requiredScripts = new List<ResourceInclude>();
if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceInclude() { Path = path, Priority = priority });
return null;
}
public static string RequireBundleStyles(this HtmlHelper html, string bundleName)
{
var a = System.Web.Optimization.Styles.Render(bundleName);
var requiredStyles = HttpContext.Current.Items["RequiredStyles"] as IHtmlString;
if (requiredStyles == null) HttpContext.Current.Items["RequiredStyles"] = requiredStyles = a;
return null;
}
public static string RequireBundleScripts(this HtmlHelper html, string bundleName)
{
var a=System.Web.Optimization.Scripts.Render(bundleName);
var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as IHtmlString;
if (requiredScripts == null) HttpContext.Current.Items["RequiredScripts"] = requiredScripts = a;
return null;
}
public static HtmlString EmitRequiredBundleStyles(this HtmlHelper html)
{
var requiredStyles = HttpContext.Current.Items["RequiredStyles"] as IHtmlString;
if (requiredStyles == null) return null;
return MvcHtmlString.Create(requiredStyles.ToHtmlString()) ;
}
public static HtmlString EmitRequiredBundleScripts(this HtmlHelper html)
{
var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as IHtmlString;
if (requiredScripts == null) return null;
return MvcHtmlString.Create(requiredScripts.ToHtmlString());
}
public static HtmlString EmitRequiredScripts(this HtmlHelper html)
{
var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as List<ResourceInclude>;
if (requiredScripts == null) return null;
StringBuilder sb = new StringBuilder();
foreach (var item in requiredScripts.OrderByDescending(i => i.Priority))
{
sb.AppendFormat("<script src=\"{0}\" type=\"text/javascript\"></script>\n", item.Path);
}
return new HtmlString(sb.ToString());
}
public class ResourceInclude
{
public string Path { get; set; }
public int Priority { get; set; }
}
}//end class
}// end namespace
PartialView 上的示例:- @Html.RequireBundleStyles("~/bundles/fileupload/bootstrap/BasicPlusUI/css"); @Html.RequireBundleScripts("~/bundles/fileupload/bootstrap/BasicPlusUI/js");
MasterPage 上的示例:- @Html.EmitRequiredBundleStyles()
使用答案 https://stackoverflow.com/a/18790222/1037948 中的 @using(Html.Delayed()){ ...your content... }
扩展来稍后在页面中呈现任何内容(脚本或只是 HTML)。内部 Queue
应确保正确排序。
此功能也在 ClientDependency.Core.Mvc.dll 中实现。它提供了 html 助手:@Html.RequiresJs 和 @Html.RenderJsHere()。 Nuget 包:ClientDependency-Mvc
这是我对常见问题“如何将部分从部分视图注入到主视图或 asp.net mvc 的主布局视图?”的解决方案。如果您通过关键字“section + partial”在 stackoverflow 上进行搜索,您将获得相当多的相关问题列表,并给出答案,但通过剃须刀引擎语法,它们对我来说似乎都不优雅。所以我只是看一下 Razor 引擎,看看是否有更好的解决方案来解决这个问题。
幸运的是,我发现 Razor 引擎如何编译视图模板文件(*.cshtml、*.vbhtml)让我很感兴趣。 (我稍后会解释),下面是我的解决方案代码,我认为它在使用上非常简单和优雅。
namespace System.Web.Mvc.Html
{
public static class HtmlHelperExtensions
{
/// <summary>
/// 确保所有视图,包括分部视图(PartialView)中的节(Section)定义被按照先后顺序追加到最终文档输出流中
/// </summary>
public static MvcHtmlString EnsureSection(this HtmlHelper helper)
{
var wp = (WebViewPage)helper.ViewDataContainer;
Dictionary<string, WebPages.SectionWriter> sw = (Dictionary<string, WebPages.SectionWriter>)typeof(WebPages.WebPageBase).GetProperty("SectionWriters", Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance).GetValue(wp);
if (!helper.ViewContext.HttpContext.Items.Contains("SectionWriter"))
{
Dictionary<string, Stack<WebPages.SectionWriter>> qss = new Dictionary<string, Stack<WebPages.SectionWriter>>();
helper.ViewContext.HttpContext.Items["SectionWriter"] = qss;
}
var eqs = (Dictionary<string, Stack<WebPages.SectionWriter>>)helper.ViewContext.HttpContext.Items["SectionWriter"];
foreach (var kp in sw)
{
if (!eqs.ContainsKey(kp.Key)) eqs[kp.Key] = new Stack<WebPages.SectionWriter>();
eqs[kp.Key].Push(kp.Value);
}
return MvcHtmlString.Create("");
}
/// <summary>
/// 在文档流中渲染指定的节(Section)
/// </summary>
public static MvcHtmlString RenderSectionEx(this HtmlHelper helper, string section, bool required = false)
{
if (helper.ViewContext.HttpContext.Items.Contains("SectionWriter"))
{
Dictionary<string, Stack<WebPages.SectionWriter>> qss = (Dictionary<string, Stack<WebPages.SectionWriter>>)helper.ViewContext.HttpContext.Items["SectionWriter"];
if (qss.ContainsKey(section))
{
var wp = (WebViewPage)helper.ViewDataContainer;
var qs = qss[section];
while (qs.Count > 0)
{
var sw = qs.Pop();
var os = ((WebViewPage)sw.Target).OutputStack;
if (os.Count == 0) os.Push(wp.Output);
sw.Invoke();
}
}
else if (!qss.ContainsKey(section) && required)
{
throw new Exception(string.Format("'{0}' section is not defined.", section));
}
}
return MvcHtmlString.Create("");
}
}
}
用法:使用代码也很简单,看起来和平时几乎一样的风格。它还支持嵌套局部视图的任何级别。 IE。我有一个视图模板链:_ViewStart.cshtml->layout.cshtml->index.cshtml->[head.cshtml,foot.cshtml]->ad.cshtml。
在 layout.cshtml 中,我们有:
在 index.cshtml 中,我们有:
@{ ViewBag.Title = "首页"; } @Html.Partial("head")
在 head.cshtml 中,我们将拥有以下代码:
@section Head{ }
在 foot.cshtml 或 ad.cshtml 中也是如此,您仍然可以在其中定义 Head 或 Foot 部分,确保在局部视图文件的末尾调用一次 @Html.EnsureSection()。这就是摆脱 asp mvc 中的问题所需要做的一切。
我只是分享我的代码片段,以便其他人可以使用它。如果您觉得它有用,请不要犹豫,为我的帖子评分。 :)