簡體   English   中英

.Net MVC如何將multipart / form-data提交到Web Api

[英].Net MVC How to submit multipart/form-data to Web Api

我想通過jquery ajax和.net api控制器向服務器提交包含值和圖像文件的表單。 但是服務器無法獲取數據,始終顯示輸入參數為空。

我添加了config.Formatters.XmlFormatter.SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("multipart/form-data")); 放入WebApiConfig.cs文件。 但這仍然行不通。

但是,有趣的是,當我將代碼塊移至AdminController.cs ,它可以工作。

在以下特定情況下,如果我將表單提交到/admin/submitnew ,則它可以正常工作。 如果提交到/api/newsnewsModel服務器上的newsModel僅接收空值。

所以我的問題是,為什么無法在apicontroller下接收/准備好apicontroller ,以及如何解決該問題。

NewsEdit.cshtml

@using (Html.BeginForm(null, null, FormMethod.Post, new { id = "editform" }))

{
    @Html.AntiForgeryToken()

<div class="form-horizontal">

    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    <div class="form-group">
        @Html.LabelFor(model => model.Title, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Title, new { htmlAttributes = new { @class = "form-control", @id = "title" } })
            @Html.ValidationMessageFor(model => model.Title, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        <Lable class="control-label col-md-2">Cover Image</Lable>
        <div class="col-md-10">
            <input type="file" name="ImgFile" class="control-label" accept="image/png, image/jpeg" />
            <br /><img src="@Model.ImgPath" style="max-width:300px" />
        </div>
    </div>
</div>

NewsEdit.js

 $("#submit").click(function (e) {
            if ($("#editform").valid()) {
                e.preventDefault();

                $.ajax({
                    url: "/admin/submitnews",
                    type: "POST",
                    data: data,
                    cache: false,
                    contentType: false,
                    processData: false,
                    async: false,
                    success: function () {
                       ****
                    },
                    error: function (e) {
                        ****
                    },
                })
            }

AdminControllers.cs

public class AdminController : Controller{
     [HttpPost]
     [ValidateInput(false)]
     public ActionResult SubmitNews(News newsModel)
     {
      //some code
     }
}

NewsController.cs

 public class NewsController : ApiController{
        [HttpPost]
        [ResponseType(typeof(News))]
        public IHttpActionResult PostNewsModel(News newsModel)
        {
          //some code    
        }
}

ApiController希望您的控制器隱式接收JSON,而Controller希望對Form Data進行相同操作。 要告訴apicontroller中的方法期望表單數據,您需要一個[FromForm]

[HttpPost]
[ResponseType(typeof(News))]
public IHttpActionResult PostNewsModel([FromForm] News newsModel)
{
          //some code    
}

前一段時間,我正在處理幾乎相同的問題。 之所以出現此行為,是因為ASP.Net WepAPI中沒有針對multipart/form-data媒體類型的“即用型” 格式化程序 (而ASP.Net MVC中卻存在一個“即開即用”的 格式化程序) )。

我不記得我遇到過SO問題,Microsoft文檔,ASP.Net源和文章的確切路徑,但這是工作結果:

創建一個HttpPostedFileMultipart類來處理發布的文件:

public class HttpPostedFileMultipart : HttpPostedFileBase
{
    public override string FileName { get; }

    public override string ContentType { get; }

    public override Stream InputStream { get; }

    public override int ContentLength => (int)InputStream.Length;

    public HttpPostedFileMultipart(string fileName, string contentType, byte[] fileContents)
    {
        FileName = fileName;
        ContentType = contentType;
        InputStream = new MemoryStream(fileContents);
    }
}

然后創建您的MediaTypeFormatter:

public class FormMultipartEncodedMediaTypeFormatter : MediaTypeFormatter
{
    private const string SupportedMediaType = "multipart/form-data";

    public FormMultipartEncodedMediaTypeFormatter()
    {
        SupportedMediaTypes.Add(new MediaTypeHeaderValue(SupportedMediaType));
    }

    // can we deserialize multipart/form-data to specific type
    public override bool CanReadType(Type type)
    {
        if (type == null) throw new ArgumentNullException(nameof(type));
        return true;
    }

    // can we serialize specific type to multipart/form-data
    public override bool CanWriteType(Type type)
    {
        if (type == null) throw new ArgumentNullException(nameof(type));
        return false;
    }

    // deserialization
    public override async Task<object> ReadFromStreamAsync(
        Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
    {
        if (type == null) throw new ArgumentNullException(nameof(type));
        if (readStream == null) throw new ArgumentNullException(nameof(readStream));

        try
        {
            // read content 
            var multipartProvider = await content.ReadAsMultipartAsync();
            // fill out model dictionary
            var modelDictionary = await ToModelDictionaryAsync(multipartProvider);
            // apply dictionary to model instance
            return BindToModel(modelDictionary, type, formatterLogger);
        }
        catch (Exception e)
        {
            if (formatterLogger == null) throw;

            formatterLogger.LogError(string.Empty, e);
            return GetDefaultValueForType(type);
        }
    }

    // fill out model dictionary
    private async Task<IDictionary<string, object>> ToModelDictionaryAsync(MultipartMemoryStreamProvider multipartProvider)
    {
        var dictionary = new Dictionary<string, object>();

        foreach (var element in multipartProvider.Contents)
        {
            // getting element name
            var name = element.Headers.ContentDisposition.Name.Trim('"');

            // if we have a FileName - this is a file
            // if not - pretend this is a string (later binder will transform this strings to objects)
            if (!string.IsNullOrEmpty(element.Headers.ContentDisposition.FileName))
                // create our HttpPostedFileMultipart instance if we have any data
                if (element.Headers.ContentLength.GetValueOrDefault() > 0)
                    dictionary[name] = new HttpPostedFileMultipart(
                        element.Headers.ContentDisposition.FileName.Trim('"'),
                        element.Headers.ContentType.MediaType,
                        await element.ReadAsByteArrayAsync()
                    );
                else
                    dictionary[name] = null;
            else
                dictionary[name] = await element.ReadAsStringAsync();
        }

        return dictionary;
    }

    // apply dictionary to model instance
    private object BindToModel(IDictionary<string, object> data, Type type, IFormatterLogger formatterLogger)
    {
        if (data == null) throw new ArgumentNullException(nameof(data));
        if (type == null) throw new ArgumentNullException(nameof(type));

        using (var config = new HttpConfiguration())
        {
            if (RequiredMemberSelector != null && formatterLogger != null)
                config.Services.Replace(
                    typeof(ModelValidatorProvider),
                    new RequiredMemberModelValidatorProvider(RequiredMemberSelector));

            var actionContext = new HttpActionContext {
                ControllerContext = new HttpControllerContext {
                    Configuration = config,
                    ControllerDescriptor = new HttpControllerDescriptor { Configuration = config }
                }
            };

            // workaround possible locale mismatch
            var cultureBugWorkaround = CultureInfo.CurrentCulture.Clone() as CultureInfo;
            cultureBugWorkaround.NumberFormat = CultureInfo.InvariantCulture.NumberFormat;

            var valueProvider = new NameValuePairsValueProvider(data, cultureBugWorkaround);
            var metadataProvider = actionContext.ControllerContext.Configuration.Services.GetModelMetadataProvider();
            var metadata = metadataProvider.GetMetadataForType(null, type);
            var modelBindingContext = new ModelBindingContext
            {
                ModelName = string.Empty,
                FallbackToEmptyPrefix = false,
                ModelMetadata = metadata,
                ModelState = actionContext.ModelState,
                ValueProvider = valueProvider
            };

            // bind our model
            var modelBinderProvider = new CompositeModelBinderProvider(config.Services.GetModelBinderProviders());
            var binder = modelBinderProvider.GetBinder(config, type);
            var haveResult = binder.BindModel(actionContext, modelBindingContext);

            // store validation errors
            if (formatterLogger != null)
                foreach (var modelStatePair in actionContext.ModelState)
                    foreach (var modelError in modelStatePair.Value.Errors)
                        if (modelError.Exception != null)
                            formatterLogger.LogError(modelStatePair.Key, modelError.Exception);
                        else
                            formatterLogger.LogError(modelStatePair.Key, modelError.ErrorMessage);

            return haveResult ? modelBindingContext.Model : GetDefaultValueForType(type);
        }
    }
}

最后,在您的WebApiConfig.Register()方法中注冊此格式化程序:

    public static void Register(HttpConfiguration config)
    {
        // ...

        // add multipart/form-data formatter
        config.Formatters.Add(new FormMultipartEncodedMediaTypeFormatter());

        // ...
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM