لماذا يعتبر Content-Type كابوسًا لكل مطور C#؟
\n\nهل سبق لك أن أرسلت طلب POST عبر HttpClient في C#، فردّ عليك الخادم بخطأ غامض 415 (Unsupported Media Type)؟ مألوف، أليس كذلك؟ غالبًا ما تكمن المشكلة في رأس واحد صغير ولكنه بالغ الأهمية — Content-Type. بدونه، لا يعرف الخادم ببساطة كيف يفسر بياناتك: هل هي JSON، XML، أم نموذج عادي؟
في هذه المقالة، سنشرح كيفية تعيين Content-Type لـ HttpClient في C# بشكل صحيح، وسنستعرض الأخطاء الأكثر شيوعًا التي يقع فيها المبتدئون (وليس فقط)، ونتعلم كيفية إرسال الطلبات بحيث يفهمك الخادم من أول كلمة. هيا بنا!
\n\nما هو Content-Type ولماذا هو مطلوب؟
\n\nContent-Type هو رأس HTTP يخبر الخادم بالتنسيق الذي تُرسل به البيانات في جسم الطلب (أو الاستجابة). إذا كنت ترسل JSON، يجب أن تقول: application/json. إذا كان XML — application/xml. إذا كانت بيانات نموذج — application/x-www-form-urlencoded.
في C#، HttpClient أداة قوية ولكنها ماكرة. يمكنها تخمين التنسيق بنفسها، وقد لا تفعل. وإذا حاولت ببساطة إضافة الرأس عبر DefaultRequestHeaders، فستفاجأ بمفاجأة غير سارة: Content-Type ليس جزءًا من رؤوس الطلب الافتراضية. إنه ينتمي فقط إلى جسم الطلب.
خطأ المبتدئ النموذجي
\n\nvar client = new HttpClient();\nclient.DefaultRequestHeaders.Add("Content-Type", "application/json"); // ❌ خطأ!\n\n\nهذا الكود سيؤدي إلى استثناء InvalidOperationException مع رسالة: "Misused header name. Make sure request headers are used with HttpRequestMessage, response ones with HttpResponseMessage, and content ones with HttpContent objects." يبدو مخيفًا، لكن الحل بسيط — تابع القراءة.
ثلاث طرق لتعيين Content-Type في C# HttpClient
\n\nالطريقة 1: استخدام StringContent (الأسهل)
\n\nالطريقة الأكثر شيوعًا وصحة هي تمرير الرأس مع جسم الطلب عبر الفئة StringContent.
using System;\nusing System.Net.Http;\nusing System.Text;\nusing System.Threading.Tasks;\n\nclass Program\n{\n static async Task Main()\n {\n var client = new HttpClient();\n var json = "{ \\"name\\": \\"PythonLib\\", \\"type\\": \\"education\\" }";\n \n // إنشاء المحتوى مع تحديد Content-Type بشكل صريح\n var content = new StringContent(json, Encoding.UTF8, "application/json");\n \n var response = await client.PostAsync("https://api.example.com/data", content);\n Console.WriteLine(await response.Content.ReadAsStringAsync());\n }\n}\n\n\nفي هذا المثال، يتم تعيين Content-Type: application/json; charset=utf-8 تلقائيًا. المعامل الثاني (Encoding.UTF8) يحدد الترميز، والثالث يحدد نوع الوسائط. وهكذا — الخادم راضٍ!
الطريقة 2: عبر ByteArrayContent (للمتقدمين)
\n\nإذا كنت تعمل مع بيانات ثنائية أو تريد تحكمًا كاملاً، استخدم ByteArrayContent.
using System.Net.Http;\nusing System.Text;\n\nvar jsonBytes = Encoding.UTF8.GetBytes("{ \\"message\\": \\"Hello\\" }");\nvar content = new ByteArrayContent(jsonBytes);\ncontent.Headers.ContentType = new MediaTypeHeaderValue("application/json");\n\nvar response = await client.PostAsync("https://api.example.com/upload", content);\n\n\nهنا نقوم بتعيين ContentType يدويًا عبر الخاصية Headers. هذه الطريقة مفيدة عندما تحتاج إلى إضافة رؤوس إضافية (مثل Content-Encoding).
الطريقة 3: HttpRequestMessage + تحكم كامل
\n\nأحيانًا تحتاج إلى إرسال طلب GET مع جسم (نعم، هذا ممكن أيضًا) أو تجاوز الرؤوس على مستوى الطلب. عندها