web-dev-qa-db-fra.com

HttpClient Multipart Form Post en C #

J'essaie de faire un post de formulaire en plusieurs parties en utilisant le HttpClient en C # et je trouve que le code suivant ne fonctionne pas.

Important:

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;

Programme complet:

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);
                }
            }
        }
    }
}

Je n'ai vraiment aucune idée pourquoi cela ne fonctionne pas. J'obtiens le fichier à publier sur le point de terminaison, mais le corps (json) n'y arrive jamais. Est-ce que je fais quelque chose de mal?

Demande de code côté serveur:

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");
        }

    }
}

Configuration de l'itinéraire:

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 }
    );

}
41
Khalid Abuhakmeh

Le problème que je vois est donc que le message de demande MultipartFormDataContent définit toujours le type de contenu de la demande sur "multipart/form-data". Endcoder json et le placer dans la requête ne "ressemble" qu'au classeur de modèle sous forme de chaîne.

Vos options sont:

  • demander à votre méthode d'action mvc de recevoir une chaîne et de la désérialiser dans votre objet
  • publier chaque propriété de votre modèle en tant que partie de formulaire
  • créez un classeur de modèle personnalisé qui traitera votre demande.
  • Découpez l'opération en deux postes, envoyez d'abord les métadonnées json, l'autre envoie le fichier. La réponse du serveur doit envoyer un identifiant ou une clé pour corréler les deux requêtes.

En lisant le document RFC et la documentation MSDN vous pourrez peut-être le faire, si vous remplacez MultipartFormDataContent par MultipartContent. Mais je n'ai pas encore testé cela.

6
Jay
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

51
Matija Grcic

Ceci est un exemple de publication de chaîne et de flux de fichiers avec HTTPClient à l'aide de MultipartFormDataContent. Le Content-Disposition et le Content-Type doivent être spécifiés pour chaque HTTPContent:

Voici mon exemple. J'espère que ça aide:

 
 privé statique vide Upload () 
 {
 
 en utilisant (var client = new HttpClient ()) 
 {
 client.DefaultRequestHeaders.Add ("User-Agent", "CBS Brightcove API Service"); 
 
 utilisant (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 ..", 
 Vidéo = nouvelle vidéo () 
 {
 Name = assetName, 
 ReferenceId = Guid.NewS (), 
 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 (chemin); 
 
 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"); 
 
 
 
 Message de tâche = client.PostAsync ("http: //api.brigh tcove.com/services/post ", contenu); 
 
 var input = message.Result.Content.ReadAsStringAsync (); 
 Console.WriteLine (input.Result); 
 Console.Read (); 
} 
} 
} 
 
6
Johnny Chu