【问题标题】:ASP.NET CORE, View model has all fields as null when passed back to ControllerASP.NET CORE,视图模型在传递回控制器时所有字段都为空
【发布时间】:2020-07-24 14:13:02
【问题描述】:

我正在尝试运行简单的 ASP.NET CORE Web 应用程序,但在允许用户通过 html 链接访问我的服务器上的文件时遇到了问题。我有一个名为“TestModel”的模型、一个名为“TestView”的视图和一个名为“AppController”的控制器。我的视图允许用户在两​​个单独的字段中输入一些文本以及从他们的硬盘驱动器中选择两个不同的文件,它将所有内容绑定到我的模型并在用户单击按钮时执行 POST。我已经验证模型是否正确地传回了我的控制器。我的控制器正确地将文件保存到我的服务器目录中的文件夹中,使用单独的服务来操作文件,并将模型返回到视图中,即当我使用调试器检查 Testview 中的“返回视图(模型)”时action 被传回的模型填充了它的所有属性。

问题是我希望视图上的两个链接指向服务器上的文件,以便用户可以单击链接并收到下载文件的提示,但我似乎无法启动它。我在 razor 中使用@Html.ActionLink() 功能指向“下载描述”操作并将模型传递给它,认为它将传递当前视图的模型,但它传递的模型的所有字段都为 null .对我做错了什么有任何见解吗?

这些是我的 AppController 中的相关操作:

        [HttpPost("~/App/TestView")]
        public IActionResult TestView(TestModel Model)
        {
            if (ModelState.IsValid)
            {
                Console.WriteLine("Something was sent back from the TestView page");
                Model.OnPostAsync();
                var x = _descLogicService.Convert(Model);
            }

            return View(Model);
        }

        public IActionResult DownloadDescriptions(TestModel model)
        {
            var cd = new ContentDispositionHeaderValue("attachment")
            {
                FileNameStar = model.DescriptionFile.FileName
            };

            Response.Headers.Add(HeaderNames.ContentDisposition, cd.ToString());

            byte[] bytes = System.IO.File.ReadAllBytes(model.MeasurementExportFile);

            using (FileStream fs = new FileStream(model.MeasurementExportFile, FileMode.Open, FileAccess.Read))
            {
                fs.Read(bytes, 0, System.Convert.ToInt32(fs.Length));
                fs.Close();
            }

            return File(bytes, "text/csv");

        }

这是我的视图模型:

 public class TestModel
    {
        [BindProperty]
        [Required]
        [MinLength(5)]
        public string Name { get; set; }
        [BindProperty]
        [Required]
        [MaxLength(10,ErrorMessage ="Input string is too long.")]
        public string MyInput { get; set; }

        [BindProperty]
        [Required]
        public IFormFile DescriptionFile { get; set; }


        [BindProperty]
        [Required]
        public IFormFile MeasurementFile { get; set; }
        public string DescriptionDirectory { get; set; } 
        public string DescriptionExportFile { get; set; } 
        public string MeasurementDirectory { get; set; } 
        public string MeasurementExportFile { get; set; }

        public async Task OnPostAsync()
        {
            var token = DateTime.Now.Ticks.ToString();

            var directory =  Directory.CreateDirectory(@"uploads\"+ token + @"\");
            DescriptionDirectory = directory.CreateSubdirectory("Descriptions").FullName + @"\";
            DescriptionExportFile = directory.CreateSubdirectory(@"Descriptions\Exports\").FullName + DescriptionFile.FileName;
            MeasurementDirectory = directory.CreateSubdirectory("Measurements").FullName + @"\";
            MeasurementExportFile = directory.CreateSubdirectory(@"Measurements\Exports\").FullName + MeasurementFile.FileName;

            var file = Path.Combine(DescriptionDirectory, DescriptionFile.FileName);
            using (var fileStream = new FileStream(file, FileMode.Create))
            {
                await DescriptionFile.CopyToAsync(fileStream).ConfigureAwait(true);
            }

             file = Path.Combine(MeasurementDirectory, MeasurementFile.FileName);
            using (var fileStream = new FileStream(file, FileMode.Create))
            {
                await MeasurementFile.CopyToAsync(fileStream).ConfigureAwait(true);
            }
        }
    }

这是视图:

@**I need to add the namespace of C# models I'm creating *@
@using FirstASPNETCOREProject.ViewModels
@*I need to identify the model which 'fits' this page, that is the properties of the model can be
    bound to entities on the view page, using "asp-for"*@
@model TestModel
@{
    ViewData["Title"] = "Page for File Uploads";
}
@section Scripts{
}

<div asp-validation-summary="ModelOnly" style="color:white"></div>
<form method="post" enctype="multipart/form-data">
    <label>Enter a Description File Name</label>
    <input asp-for="Name" type="text" />
    <span asp-validation-for="Name"></span>
    <br>
    <label>Select a Description File</label>
    <input asp-for="DescriptionFile" type="file" />
    <span asp-validation-for="DescriptionFile"></span>
    <br>
    <label>Enter the Measurement File Name</label>
    <input asp-for="MyInput" type="text">
    <span asp-validation-for="MyInput"></span>
    <br>
    <label>Select a Measurement File</label>
    <input asp-for="MeasurementFile" type="file">
    <span asp-validation-for="MeasurementFile"></span>
    <br>
    <input type="submit" value="Send Message" />
</form>


@Html.ActionLink("Description File", "DownloadDescriptions", "App")
@Html.ActionLink("Measurement File", "DownloadMeasurements", "App")

【问题讨论】:

  • 欢迎来到 StackOverflow。您不能使用 ActionLink 来传递模型。您需要使用第二种和第三种形式,其中包含要发布到下载操作的值。那,或者您创建一个 GET 请求并提供您需要的值作为路由参数。后者可以通过 ActionLink 完成。
  • @Dennis1679 这不是真的...您可以使用 ActionLinks 传递模型...您只需在 ActionLink 调用中添加一个匿名类型并将参数设置为控制器中的 [FromQuery]行动
  • @JonathanAlfaro 您所描述的并不是像我所说的“通过”那样传递模型。我想说的是,你不能通过 ActionLink 传递模型,就像你通过表单/帖子传递它一样。但是,您可以 - 正如您所描述的那样 - 欺骗它从模型创建一个 RouteValueDictionary ,因为 ActionLink 将采用一个对象,而该对象又将被转换为查询字符串。这让我想知道,这是否适用于包含列表的复杂模型?
  • @Dennis1679 你又错了....使用查询字符串参数绑定复杂对象是完全可能的....事实上,我只是发布了一个关于如何做到这一点的示例.
  • 仅供参考,这个组合对我有用:在视图中:@Html.ActionLink("Measurement File", "DownloadMeasurements", "App", Model)。在控制器中:public IActionResult DownloadMeasurements([FromQuery]TestModel model){ }。视图中的模型是一个 TestModel 对象。

标签: c# asp.net-mvc asp.net-core model-view-controller mvvm


【解决方案1】:

ActionLink 只是锚标记。

所以他们使用 GET。因此,要使用 get 传回您的模型,您需要使用查询字符串参数,例如在 url 末尾添加“?MyInpu=some cool input”。

您可以绑定几乎任何这样的复杂对象,包括列表和数组。

有关Model Binding的更多信息

文件本身你将无法像那样传递它。为此,您需要使用 javascript 使用提交按钮或 FormData 发布表单。

您还可以添加调用 javascript 函数的锚点,这些函数使用 AJAX 将您想要的所有内容回发到控制器中的 DownloadDescriptions 操作。

这是一个关于如何使用操作链接传递模型的示例:

 @Html.ActionLink("Test Action", "TestAction", "Home", new { myInput = "Some Cool Input" })

在我的例子中,之前的 ActionLink 生成了一个带有 href 设置为的锚标记:

http://localhost:64941/Home/TestAction?myInput=Some Cool Input

注意我是如何使用匿名类型来传递模型的,在这种情况下 MyInput 但在骆驼化版本 myInput 中使用与模型属性相同的名称。。 p>

你可以像这样组成任何模型。

这是我在控制器中的操作:

    public IActionResult TestAction([FromQuery]TestModel input) 
    {
        return View(input);
    }

请注意我如何将 [FromQuery] 用于 TestModel 参数,以表明我希望 ASP.NET Core 模型绑定器使用查询字符串参数来填充我的模型。

这是我的模型类:

public class TestModel
{
    public string MyInput { get; set; }
}

这是调试时的结果:

注意在调试过程中我如何能够看到填充的值。

注意事项:

如果您的模型在客户端发生更改。您将需要使用 javascript 更新锚标记中的查询字符串参数...因此,将名称添加到锚标记是一个好主意。

这也可能回答您的问题,但可能不是您尝试做的最佳方法。

【讨论】:

  • 我尝试支持您的答案,但 StackOverflow 说我是一个声名狼藉的人!非常感谢您的详细解答!我根据您的建议尝试了两种方法,它们都可以正常工作。在我看来:``` @Html.ActionLink("Description File", "DownloadDescriptions", "App", new {file = Model.DescriptionExportFile}) @Html.ActionLink("Measurement File", "DownloadMeasurements", "App ", Model) ``` 和控制器 ``` public IActionResult DownloadDescriptions(string file) public IActionResult DownloadMeasurements([FromQuery]TestModel model) ```
猜你喜欢
  • 2021-02-24
  • 1970-01-01
  • 1970-01-01
  • 2017-07-15
  • 1970-01-01
  • 1970-01-01
  • 2019-12-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多