如何提供采用多部分表单数据或json内容的单个Web Api端点
本文关键字:单个 Web 端点 Api json 何提供 多部 数据 表单 | 更新日期: 2023-09-27 18:24:04
我正在构建一个WebApi,无论客户端发布的是多部分表单数据还是使用json内容的帖子,我都希望为每个帖子提供一个端点。
编辑:代码可以处理多部分表单数据或json。无论内容类型如何,我都希望为客户提供一个可以点击的url。张贴狗应该是张贴一只狗,不管怎样。
编辑2:唯一的问题是将dogDTO参数设置为null(dogDTO dog=null)。如果控制器方法签名如下所示,则工作正常。
public async Task<IHttpActionResult> PostDog(DogDTO dog)
我将以狗为例。我有一只狗
public class DogDTO
{
[Key]
public int DogId {get;set;}
[Required]
public string Name {get;set}
public string Breed {get;set}
public byte[] FileAboutDog {get;set}
}
我希望Controller方法看起来像这样:(假装错误处理和其他有用的东西):
[ResponseType(typeof(DogDTO))]
[HttpPost]
[Route("api/Dogs")]
public async Task<IHttpActionResult> PostDog(DogDTO dog = null)
{
if (Request.Content.IsMimeMultipartContent())
{
//Parse the Form Data to build the dog
return await PersistDogFormData();
}
//Use standard json Content
else return PersistDog(dog);
}
使用上面的代码,我得到以下错误(在这个问题中提到,没有答案):
{"Message":"发生错误。","ExceptionMessage":"可选参数"dog"不受"FormatterParameterBinding"支持。","ExceptionType":"System.InvalidOperationException","StackTrace":"位于System.Web.Http.Controllers.HttpActionBinding.ExecuteBindingAsync(HttpActionContext actionContext,CancellationToken cancellionToken)''r''n位于System.Web.Http.Controllers.ActionFilterResult.d_2.MoveNext()''r''n-从引发异常的前一位置开始的堆栈跟踪结束---''r''n位于System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)''r''n位于System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()"}
是否有其他方法可以提供单个端点?我不能使用方法重载,因为我得到了不明确的路由运行时错误。
非常感谢。
您应该使用自定义的MediaTypeFormatter。更多信息请点击此处:http://www.asp.net/web-api/overview/formats-and-model-binding/media-formatters
public class CustomMediaTypeFormatter : MediaTypeFormatter
{
public CustomMediaTypeFormatter ()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data"));
}
public override bool CanReadType(Type type)
{
return type == typeof (DogDTO);
}
public override bool CanWriteType(Type type)
{
return false;
}
public async override Task<object> ReadFromStreamAsync(
Type type,
Stream readStream,
HttpContent content,
IFormatterLogger formatterLogger)
{
var provider = await content.ReadAsMultipartAsync();
var modelContent = provider.Contents
.FirstOrDefault(c => c.Headers.ContentDisposition.Name.NormalizeName() == "dog");
var dogDTO = await modelContent.ReadAsAsync<DogDTO>();
var fileContent = provider.Contents
.Where(c => c.Headers.ContentDisposition.Name.NormalizeName() == "image"))
.FirstOrDefault();
dogDTO.FileAboutDog = await fileContent.ReadAsByteArrayAsync();
return dogDTO;
}
}
public static class StringExtensions
{
public static string NormalizeName(this string text)
{
return text.Replace("'"", "");
}
}
注册自定义媒体格式化程序:
public static void ConfigureApis(HttpConfiguration config)
{
config.Formatters.Add(new CustomMediaTypeFormatter());
}
更新客户端代码,因为JSON序列化需要忽略文件,因为它将位于单独的请求部分:
public class Dog
{
public string Name {get;set}
public string Breed {get;set}
[JsonIgnore]
public byte[] FileAboutDog {get;set}
}
POST示例:
POST http://www.example.com/
Content-Type: multipart/form-data; boundary=-------------------------acebdf13572468
---------------------------acebdf13572468
Content-Type: application/json; charset=utf-8
Content-Disposition: form-data; name=dog
{"name":"DogName", "breed":"DogBreed"}
---------------------------acebdf13572468
Content-Disposition: form-data; name="image"; filename="image.jpg"
Content-Type: image/jpeg
image content
---------------------------acebdf13572468--
请求的Json部分必须命名为:"dog",服务器用这个名称查找Json部分。图像部分必须命名为"image"。您可以通过检查内容类型而不是查找名称来删除此限制。
我还没有测试代码,您可能需要进行一些调整。