.Net MVC Как отправить multipart / form-данные в Web Api - PullRequest
0 голосов
/ 05 июля 2019

Я хочу отправить форму, содержащую значения и файл изображения на сервер, с помощью jquery ajax и .net api controller.Но сервер не может получить данные, всегда показывая нулевой входной параметр.

Я добавил config.Formatters.XmlFormatter.SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("multipart/form-data")); в WebApiConfig.cs файл.Но он все равно не работает.

Однако интересно то, что когда я перемещаю свой блок кода в мой AdminController.cs, он работает.

В следующем конкретном случае, если я отправляюформа до /admin/submitnew, работает отлично.Если отправить на /api/news, newsModel на сервере, получит только нулевое значение.

Итак, мой вопрос, почему данные не могут быть получены / готовы под 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    
        }
}

Ответы [ 2 ]

0 голосов
/ 05 июля 2019

Некоторое время назад я занимался почти той же проблемой. Причина, по которой вы получаете такое поведение, заключается в том, что в ASP.Net нет "готового" форматера для multipart/form-data медиа-типа. WepAPI (между тем, в 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());

        // ...
    }
0 голосов
/ 05 июля 2019

ApiController ожидает, что ваши контроллеры неявно получат JSON, в то время как Controller ожидает, что делает то же самое с данными формы.Чтобы сообщить вашим методам в apicontroller ожидать данные формы, вам понадобится [FromForm]

[HttpPost]
[ResponseType(typeof(News))]
public IHttpActionResult PostNewsModel([FromForm] News newsModel)
{
          //some code    
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...