مرجع تخصصی برنامه نویسان

انجمن تخصصی برنامه نویسان فارسی زبان

کاربر سایت

payam64

عضویت از 1397/01/14

ارسال درخواست post با httpclient

  • جمعه 23 خرداد 1399
  • 13:35
تشکر میکنم

با سلام داخل یک وب اپلیکیشن که با تکنولوژی دات نت کور نوشته شده از دو پروژه یکی برای وب و دیگری برای web api استفاده شده

به چه شکل میشه درخواستی را با استفاده از httpclient برای ارسال مدل به همراه یک فایل برای web api ارسال کرد ؟

نمونه کد موجود که در پاسخ 415 unhandled media type را برمیگردونه

await using (var fs = System.IO.File.OpenRead(model.ImagePath))
                {
                    imageBytes= new byte[fs.Length];
                    fs.Read(imageBytes, 0, buffer.Length);
                    fs.Close();
                }

if (imageBytes?.Length == 0)
                    {
                         return null;
                    }
                    var multipartContent = new MultipartFormDataContent();
                    //    here you can specify boundary if you need---^
                    var imageContent = new ByteArrayContent(imageBytes);
                    imageContent.Headers.ContentType =
                        MediaTypeHeaderValue.Parse("image/jpeg");
                    multipartContent.Add(imageContent, "image", "image.jpg");

                    var jsonObject = JsonConvert.SerializeObject(model);
                    sc = new StringContent(
                        jsonObject, Encoding.UTF8, "multipart/form-data");
                    multipartContent.Add(sc);
                    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                    responseMessage = await client.PostAsync(uriPath, sc);
                    if (!responseMessage.IsSuccessStatusCode)
                    {
                        _alert.SetAlert(false, "خطا در ثبت داده");
                        return null;
                    }

پاسخ های این پرسش

تعداد پاسخ ها : 1 پاسخ
کاربر سایت

payam64

عضویت از 1397/01/14

  • پنجشنبه 29 خرداد 1399
  • 16:16

با سلام خدمت همه دوستان
بعد از دو روز کلی جستجو و زیر و رو کردن خدا رو شکر مشکل اوکی شد و تصمیم گرفتم با شما به اشتراک بزارم ممنون میشم حتما وقت بزارین و مطالعه کنین
قبلش اشاره کنم که من از .net core 3.0 استفاده میکنم و در سولوشن تمامی سرویس های rest در یک پروژه جدا قرار داره که به وسیله بک پروژه .net core mvc دیگه قراره این سرویس های طراحی شده استفاده بشه
مشکل اصلی به وجود آمده در نوع model binding کردن بود که در هر پروژه ای با تکنولوژی asp.net core بصورت پیشفرض مقادیر داده ای مورد نظر به وسیله یک جفت کلید و مقدار به وسیله منابع مختلفی که در زیر اشاره میکنم تامین میشه bind میشن


فیلد ها فرم

request body یا همون بدنه اصلی درخواست ارسالی که این نوع مخصوص ارسال داده به وسبله کنترلر های web api که دارای خصوصیت [ApiController] هستند ارسال میشه

به وسبله route data یعنی مفادیر از طریق پارامترهای تعریف شده در سیستم مسیریابی bind میشن

query string ها که نیازی به معرفی نداره

فایل های آپلود شده برای درخواست جاری


حالا نسبت به هر درخواست دریافتی به ترتیب نام برده شده در لیست چک شده و داده های ارسالی bind میشن اما در روش های نام برده مثل route data و query string و همچنین برای آپلود فایل دو مشکل اصلی وجود داره که :
اول اینکه داده ارسالی باید برای انواع داده ای ساده و پایه استفاده بشن و برای انواع داده های پیچیده تر مثل یک data model قابلیت استفاده شده و bind کردن ندارن
دوم اینکه برای ارسال فایل شما فقط باید در پارامتر ورودی متد از یک IFormFile میتونین استفاده کنین و به دلیل مشکل قبلی که توضیح داده شده امکان ارسال مدل به همراه فایل وجود نداره

راه حل مشکل برای این استفاده کردن از یکسری خصوصیات برای مشخص کردن داده ارسالی هست

FromQuery

FromBody

FromForm

FromRoute

FromHeader


همونجوز که از اسم این حصوصیات مشخصه هر کدوم برای ارسال داده به روش های مختلف و bind کردن داده های ارسالی استفاده میشن مثلا از FromRoute برای دریافت داده های ارسالی در قالب پارامترهای تعریف شده در سیستم مسیر یابی و یا از fromquery برای ارسال querystring جهت bind شدن استفاده میشه

اما بریم سراغ خصوصیت اصلی که مشکل مارو حل کرد استفاده از FromForm هست بعنی پارامترهای ارسالی و داده های ارسالی باید در بدنه درخواست خودشون form-data بکار برده باشند که با استفاده از این حصوصیت امکان ارسال همزمان مدل به همراه فایل در قالب یک درخواست واحد وجود داره. کد درخواسنی ارسالی با استفاده از httpclient

using var client = new HttpClient
            {
                BaseAddress = new Uri("http://localhost:5773/")
                HttpResponseMessage responseMessage;

            if (file?.Length > 0)
            {
                using (var content = new MultipartFormDataContent())
                {
                    content.Add(new StreamContent(file.OpenReadStream())
                    {
                        Headers =
                {
                ContentLength = file.Length,
                ContentType = new MediaTypeHeaderValue(file.ContentType)
            }
                    }, "file", file.FileName);
 
 
                    foreach (var property in model.GetType().GetProperties())
                    {
                        content.Add(new StringContent(
                            property.GetValue(model, null)?.ToString() ?? ""), property.Name);
                    }
                    responseMessage = client.PostAsync(uriPath, content).Result;
                }
            }
            else
            {
                var jsonObject = JsonConvert.SerializeObject(model);
                sc = new StringContent(
                    jsonObject, Encoding.UTF8, "application/json");
                responseMessage = await client.PostAsync(uriPath, sc);
            }
            var result = await responseMessage.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<T>(result);
        };

مهمترین قسمت در کد بالا مربوط به تعریف یک نمونه درخواست از نوع MultipartFormDataContent هست که با استفاده از متد اصلی Add شمh قادر به اضافه کردن فایل و همچنین مدل داده خودتون هستین در متد Add اول برای اضافه کردن فایل که بصورت استریم دریافت میشه و سپس جون متد اصلی بصورت generic تعریف شده با استفاده از reflection شما تمامی پراپرتی های هر مدلی رو میتونین براش تعریف کنین ولی دقت داشته باشین در حلقه foreach به ازای هر یک از پراپرتی های مدل از یک متد add استفاده شده و در نهایت درخواست با استفاده از متد PostAsync ارسال میشه

نکته اول در رمان اضافه کردن فایل استریم شده نام فایل ارسالی بصورت پیشفرض file در نظر گرقته شده که در سمت سرویس با همین نام دریافت میشه

نکته مهم بعدی در مورد if else بکار برده شده برای بررسی اینکه مدل ارسالی حاوی فایلی برای ارسال هست یا خیر چون درخواست هایی که فقط شامل data model هستن در صورت ارسال با حالت form-data در سمت سرویس بصورت Null دریافت میشن دلیلش هم مشخصه چون بدنه درخواست حاوی form-data هست بنابراین باید شامل یک استریم دریافتی از سمت client باشه. بنابراین در قسمت else درخواست بصورت یک درخواست عادی serialize شده و برای سرویس rest ارسال میشه

کد مورد نظر برای سمت سرویس rest برای دریافت درخواست

[HttpPost]
       public async Task<ActionResult<BlogCategory>> PostNewBlogCategory(
           [FromForm] DataModel model, [FromForm] IFormFile file)
       {
           if (!ModelState.IsValid) return BadRequest(ModelState);
           if (files?.Length > 0)
           {
               // save file
           }
           // save model
           return Created(new Uri("uri address"));
       }
در مورد متد اکشن سرویس اگه دفت کنین مدل و فایل ارسالی بصورت جدا با استفاده از خصوصیت FromForm دریافت میشن ولی دقت کنین که برای درخواست های معمولی که فقط شامل data model هست باید از FromBody استفاده کنین یا اصلا میتونین هیچی قرار ندین
کاربرانی که از این پست تشکر کرده اند

هیچ کاربری تا کنون از این پست تشکر نکرده است

اگر نیاز به یک مشاور در زمینه طراحی سایت ، برنامه نویسی و بازاریابی الکترونیکی دارید

با ما تماس بگیرید تا در این مسیر همراهتان باشیم :)