2013-08-05 18 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」 - >這節省了我 –