![](/img/trans.png)
[英]how to send request from Asp.net MVC to external api in multipart/form-data type
[英].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/news
, newsModel
服務器上的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.