在AJAX中向ASP.NET WebAPI2发布包含属性和多个附件的表单

时间:2016-03-11 12:22:48

标签: c# jquery asp.net ajax asp.net-web-api2

我在视图上有一组带有一组输入的常规表单。我使用jQuery来序列化并发布它。

$("#formId").submit(function (e) {
 e.preventDefault();

 var fields = {};

 var formSerialized = $("#formId").serializeArray();

 $.each(formSerialized, function (i, field) {
  fields[field.name] = field.value; });
 e.data = fields;

 $.post("myUrl", {
   FirstName: e.data.FirstName,
   LastName: e.data.LastName
            }, function (success) {
                if (success) {
                    alert('Ok')
                } else {
                    alert('fail');
                }
            });
        }
    });

在后端,我有ASP.NET WebAPI2服务器,其中包含获取此请求的操作,并自动将所有属性绑定到模型。

现在我需要将多个文件输入添加到同一表单中。

有没有办法: - 在同一个请求中同时发送我在代码示例中发送的文件和常规属性? - 是否可以使用HttpPostedFileBase属性在WebAPI端扩展模型并保持数据的自动绑定?

在同一个请求中发送常规文本属性(表单中的值)+多个文件以在WebAPI2端的单个方法中处理它们的最佳方法是什么?

谢谢!

2 个答案:

答案 0 :(得分:1)

此答案讨论了一些选项,但您可能会遇到一些浏览器兼容性问题:

jQuery Ajax File Upload

如果是一个选项,您可以考虑定期进行非异步表单提交,并确保您的表单具有enctype =“multipart / form-data”,但如果没有,您可以尝试该链接中讨论的一些内容。希望这有帮助

编辑 - 另一种方法是使用这个jQuery表单插件:http://malsup.com/jquery/form/

在这种情况下,我没有使用viewmodel,而是将输入直接绑定到参数,但我想不出任何理由为什么它与典型的vm完全不同。

使用示例:

前端

    @using (Html.BeginForm("SaveComment", "Comments", FormMethod.Post, new { enctype = "multipart/form-data" }))
    {

        [text and file inputs, submit button]
    }

<script>
$('#addNote form').ajaxForm({
    success: function (response) {
        $.notify(response.result, {
            className: "success",
            position: "top center"
        });

        //reload comments section
        $.ajax({
            url: './Comments/Index',
            data: {
                labelId: '@Model.LabelId',
                orderNumber: '@Model.OrderNumber'
            },
            success: function (response) {
                $('#commentsSection').html(response);
            }
        });
    },
    error: function () {
        $.notify("Failed to save note");
    },
    beforeSubmit: function () {
        $('#addNote').modal('toggle');
    }
});

后端

public JsonResult SaveComment(string saveNote, string labelId, string orderNumber, string comment, string criteria, HttpPostedFileBase file)
    {
       [text input named saveNote went into saveNote param]
       [file input named file went into HttpPostedFileBase file param]

       [...process other params...]

        var ms = new MemoryStream();
        file.InputStream.CopyTo(ms);
        deliveryItemComment.Attachment = ms.ToArray();
        db.SaveChanges();

        var result = "Note Succesfully Added";
        return Json(new { result = result }, JsonRequestBehavior.AllowGet);
    }

答案 1 :(得分:0)

我想是非常丑陋的解决方案,但我以这种方式结束了。

JS使用表单数据:

            var formSerialized = $("formId").serializeArray();

            var data = new FormData();
            var files = $("#fileInputId").get(0).files;
            data.append("File", files[0]);

            $.each(formSerialized, function (i, field) {
                data.append(field.name, field.value);
            });

            $.ajax({
                type: "POST",
                contentType: false,
                processData: false,
                url: "some URL",
                data: data
                }
            });

在WebAPI方面,我必须分别从表单数据中读取表单字段和文件:

        var myModel = new MyModel();

        var root = HttpContext.Current.Server.MapPath("~/App_Data/");
        var provider = new MultipartFormDataStreamProvider(root);

        await Request.Content.ReadAsMultipartAsync(provider);

        //Maps fields from form data into my custom model
        foreach (var key in provider.FormData.AllKeys)
        {
            var value = provider.FormData.GetValues(key).FirstOrDefault();
            if (value != null && value != "undefined")
            {
                var prop = myModel.GetType().GetProperty(key);

                if (prop != null)
                {
                    prop.SetValue(myModel, value, null);
                }
            }
        }

          //Resaves all files in my custom location under App_Data and puts their paths into list
            var fileNames = new Collection<string>();
           foreach (var file in provider.FileData)
            {
                var fileExt = file.Headers.ContentDisposition.FileName.Split('.').Last().Replace("\"", string.Empty);
                var combinedFileName = string.Format("{0}.{2}", file.Headers.ContentDisposition.Name.Replace("\"", string.Empty), fileExt);
                var combinedPath = Path.Combine(root + "CustomDirectory/", combinedFileName);
                File.Move(file.LocalFileName, combinedPath);
                fileNames.Add(combinedPath);
            }