2013-08-05 17 views
33

我想要做一个多部分形式的帖子在C#中使用HttpClient,我发现下面的代码不起作用。C#中的HttpClient多部分形式发布#

重要:

var jsonToSend = JsonConvert.SerializeObject(json, Formatting.None, new IsoDateTimeConverter()); 
var multipart = new MultipartFormDataContent(); 
var body = new StringContent(jsonToSend, Encoding.UTF8, "application/json"); 

multipart.Add(body); 
multipart.Add(new ByteArrayContent(File.ReadAllBytes("test.txt")), "test", "test.txt"); 

var httpClient = new HttpClient(); 
var response = httpClient.PostAsync(new Uri("http://localhost:55530"), multipart).Result; 

完整程序

namespace CourierMvc.Worker 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      while (true) 
      { 
       Console.WriteLine("Hit any key to make request."); 
       Console.ReadKey(); 

       try 
       { 
        var request = new RestRequest(Method.POST) 
        { 
         Resource = "http://localhost:55530" 
        }; 

        var json = new CourierMessage 
        { 
         Id = Guid.NewGuid().ToString(), 
         Key = "awesome", 
         From = "[email protected]", 
         To = new[] { "[email protected]", "[email protected]" }, 
         Subject = "test", 
         Body = "body", 
         Processed = DateTimeOffset.UtcNow, 
         Received = DateTime.Now, 
         Created = DateTime.Now, 
         Sent = DateTime.Now, 
         Links = new[] { new Anchor { Link = "http://google.com" }, new Anchor { Link = "http://yahoo.com" } } 
        }; 

        var jsonToSend = JsonConvert.SerializeObject(json, Formatting.None, new IsoDateTimeConverter()); 
        var multipart = new MultipartFormDataContent(); 
        var body = new StringContent(jsonToSend, Encoding.UTF8, "application/json"); 

        multipart.Add(body); 
        multipart.Add(new ByteArrayContent(File.ReadAllBytes("test.txt")), "test", "test.txt"); 

        var httpClient = new HttpClient(); 
        var response = httpClient.PostAsync(new Uri("http://localhost:55530"), multipart).Result; 

       } 
       catch (Exception e) 
       { 
        Console.WriteLine(e); 
       } 
      } 
     } 
    } 
} 

我真的不知道为什么它不工作。我得到文件发布到端点,但body(json)永远不会到达那里。难道我做错了什么?

服务器端代码请求:

namespace CourierMvc.Controllers 
{ 
    public class HomeController : Controller 
    { 
     // 
     // GET: /Home/ 

     public ActionResult Index() 
     { 
      return Content("Home#Index"); 
     } 


     [ValidateInput(false)] 
     public ActionResult Create(CourierMessage input) 
     { 
      var files = Request.Files; 

      return Content("OK"); 
     } 

    } 
} 

路由配置:

public static void RegisterRoutes(RouteCollection routes) 
{ 
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 

    routes.MapRoute(
     name: "Default", 
     url: "{controller}/{action}/{id}", 
     defaults: new { controller = "Home", action = "Create", id = UrlParameter.Optional } 
    ); 

} 
+1

为什么有人会投这个票?我提供了代码,并解释了我想要做的事情。这是一个明确的说法。 –

+0

如果您也显示服务器端代码,那么可能会有所帮助,因此我们可以看到您如何尝试读取它。 –

+0

服务器端代码只是一个ASP.NET MVC端点,其中模型作为输入类型。那里没什么特别的,但我放下来表示没有诡计。 –

回答

4

所以我看到的问题是,MultipartFormDataContent请求消息总是会设置内容类型的对“multipart/form-data”的请求。对json进行端点编码并将其放入请求中只会看起来像模型活页夹一样是一个字符串。

的选项有:

  • 有你的MVC操作方法得到的字符串和反序列化到你的对象
  • 后模型的每个属性作为一个组成部分
  • 创建一个自定义的模型绑定即会处理您的请求。
  • 将操作分解为两个帖子,首先发送json元数据,另一个发送文件。来自服务器的响应应发送一些id或密钥来关联这两个请求。

通过the RFC document阅读和the MSDN documentation你可以做到这一点,如果你有MultipartContent替换MultipartFormDataContent。但我还没有测试过。

+0

尝试MultipartContent,并没有工作。我真的不想做你的第一个建议,但看起来更有可能是这样。 –

+0

对于希望将字符串内容作为表单值添加的HttpClient,而不是将其作为帖子的另一部分发布。不知道这是否是HttpClient中的错误,或者是我的根本误解。 –

+0

好吧,通常当你发布一个文件的时候,内容类型是'multipart/form-data',就像任何表单数据类型一样,post只是一个关键值对序列化。当你添加你的json时,它只是另一个对,而不是MVC中模型绑定器的序列化对象。 – Jay

40
public class CourierMessage 
{ 
    public string Id { get; set; } 
    public string Key { get; set; } 
    public string From { get; set; } 
    public string Subject { get; set; } 
    public string Body { get; set; } 
    public DateTimeOffset Processed { get; set; } 
    public DateTime Received { get; set; } 
    public DateTime Created { get; set; } 
    public DateTime Sent { get; set; } 
    public HttpPostedFileBase File { get; set; } 
} 




while (true) 
{ 
    Console.WriteLine("Hit any key to make request."); 
    Console.ReadKey(); 

    using (var client = new HttpClient()) 
    { 
     using (var multipartFormDataContent = new MultipartFormDataContent()) 
     { 
      var values = new[] 
      { 
       new KeyValuePair<string, string>("Id", Guid.NewGuid().ToString()), 
       new KeyValuePair<string, string>("Key", "awesome"), 
       new KeyValuePair<string, string>("From", "[email protected]") 
       //other values 
      }; 

      foreach (var keyValuePair in values) 
      { 
       multipartFormDataContent.Add(new StringContent(keyValuePair.Value), 
        String.Format("\"{0}\"", keyValuePair.Key)); 
      } 

      multipartFormDataContent.Add(new ByteArrayContent(File.ReadAllBytes("test.txt")), 
       '"' + "File" + '"', 
       '"' + "test.txt" + '"'); 

      var requestUri = "http://localhost:5949"; 
      var result = client.PostAsync(requestUri, multipartFormDataContent).Result; 
     } 
    } 
} 

enter image description here

+0

尝试在您的示例中添加复杂列表,看看它是否仍然有效。通过一个复杂的列表,我的意思是锚对象列表。我看到你删除了他们......我不认为这是你的一部分的意外:) –

+0

@KhalidAbuhakmeh我没有添加剩余的属性,因为我只是想给你提供一个基于你的初始化题。 –

+1

您可以通过这种方式发布复杂对象(List)的唯一方法是将它们序列化为逗号分隔列表或JSON blob。这仍然是一个很好的解决方案。 –

5

这是如何使用MultipartFormDataContent与发布的HTTPClient字符串和文件流的例子。 Content-Disposition和Content-Type需要为每个HTTPContent指定:

这是我的例子。希望它有帮助:

 

private static void Upload() 
     { 

      using (var client = new HttpClient()) 
      { 
       client.DefaultRequestHeaders.Add("User-Agent", "CBS Brightcove API Service"); 

       using (var content = new MultipartFormDataContent()) 
       { 
        var path = @"C:\B2BAssetRoot\files\596086\596086.1.mp4"; 

        string assetName = Path.GetFileName(path); 

        var request = new HTTPBrightCoveRequest() 
         { 
          Method = "create_video", 
          Parameters = new Params() 
           { 
            CreateMultipleRenditions = "true", 
            EncodeTo = EncodeTo.Mp4.ToString().ToUpper(), 
            Token = "x8sLalfXacgn-4CzhTBm7uaCxVAPjvKqTf1oXpwLVYYoCkejZUsYtg..", 
            Video = new Video() 
             { 
              Name = assetName, 
              ReferenceId = Guid.NewGuid().ToString(), 
              ShortDescription = assetName 
             } 
           } 
         }; 

        //Content-Disposition: form-data; name="json" 
        var stringContent = new StringContent(JsonConvert.SerializeObject(request)); 
        stringContent.Headers.Add("Content-Disposition", "form-data; name=\"json\""); 
        content.Add(stringContent, "json"); 


        FileStream fs = File.OpenRead(path); 

        var streamContent = new StreamContent(fs); 
        streamContent.Headers.Add("Content-Type", "application/octet-stream"); 
        streamContent.Headers.Add("Content-Disposition", "form-data; name=\"file\"; filename=\"" + Path.GetFileName(path) + "\""); 
        content.Add(streamContent, "file", Path.GetFileName(path)); 

        //content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); 



        Task message = client.PostAsync("http://api.brightcove.com/services/post", content); 

        var input = message.Result.Content.ReadAsStringAsync(); 
        Console.WriteLine(input.Result); 
        Console.Read(); 
       } 
      } 
     } 

+0

谢谢。这节省了我一小时的搜索后如何把数据放在请求的http表单中: stringContent.Headers.Add(“Content-Disposition”,“form-data; name = \”json \“”); 现在我的Json对象非常容易访问。 this.Request.Form [ “JSON”]; –

+0

“需要为每个HTTPContent指定Content-Disposition和Content-Type” - >这节省了我 –