如何提供采用多部分表单数据或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()"}

是否有其他方法可以提供单个端点?我不能使用方法重载,因为我得到了不明确的路由运行时错误。

非常感谢。

如何提供采用多部分表单数据或json内容的单个Web Api端点

您应该使用自定义的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"。您可以通过检查内容类型而不是查找名称来删除此限制。

我还没有测试代码,您可能需要进行一些调整。