Wie legen Sie den Content-Type-Header für eine HttpClient-Anforderung fest?

739

Ich versuche, den Content-TypeHeader eines HttpClientObjekts so festzulegen, wie es für eine von mir aufgerufene API erforderlich ist.

Ich habe versucht, Content-TypeFolgendes einzustellen:

using (var httpClient = new HttpClient())
{
    httpClient.BaseAddress = new Uri("http://example.com/");
    httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
    httpClient.DefaultRequestHeaders.Add("Content-Type", "application/json");
    // ...
}

Ich kann den AcceptHeader hinzufügen, aber wenn ich versuche, ihn hinzuzufügen Content-Type, wird die folgende Ausnahme ausgelöst:

Missbrauchter Headername. Stellen Sie sicher, dass Anforderungsheader mit HttpRequestMessage, Antwortheader mit HttpResponseMessageund Inhaltsheader mit HttpContentObjekten verwendet werden.

Wie kann ich den Content-TypeHeader in einer HttpClientAnfrage setzen?

mynameiscoffey
quelle
Sie können verfolgen, wie HttpWebRequest in .NET Core dies tut (es verwendet intern HttpClient), siehe github.com/dotnet/corefx/blob/master/src/System.Net.Requests/… "SendRequest" -Methode
jiping-s

Antworten:

928

Der Inhaltstyp ist eine Kopfzeile des Inhalts und nicht der Anforderung, weshalb dies fehlschlägt. AddWithoutValidationWie von Robert Levy vorgeschlagen, funktioniert dies möglicherweise, aber Sie können den Inhaltstyp auch festlegen, wenn Sie den Anforderungsinhalt selbst erstellen (beachten Sie, dass das Code-Snippet an zwei Stellen "application / json" hinzufügt - für Header "Akzeptieren" und "Inhaltstyp"):

HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://example.com/");
client.DefaultRequestHeaders
      .Accept
      .Add(new MediaTypeWithQualityHeaderValue("application/json"));//ACCEPT header

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "relativeAddress");
request.Content = new StringContent("{\"name\":\"John Doe\",\"age\":33}",
                                    Encoding.UTF8, 
                                    "application/json");//CONTENT-TYPE header

client.SendAsync(request)
      .ContinueWith(responseTask =>
      {
          Console.WriteLine("Response: {0}", responseTask.Result);
      });
carlosfigueira
quelle
32
Alternativ Response.Content.Headersfunktioniert die meiste Zeit.
John Gietzen
4
@AshishJain Die meisten SO-Antworten, die ich Response.Content.Headersfür die ASP.Net-Web-API gesehen habe, haben auch nicht funktioniert, aber Sie können sie HttpContext.Current.Response.ContentTypebei Bedarf einfach festlegen .
Jerhewet
6
@ Jerhewet Ich habe auf folgende Weise verwendet, was für mich funktioniert hat. var content = new StringContent (Daten, Encoding.UTF8, "application / json");
Ashish-BeJovial
22
Der Inhaltstyp ist eine Eigenschaft einer HTTP-Nachricht mit Nutzdaten. es hat nichts mit Anfrage oder Antwort zu tun.
Julian Reschke
6
Interessant. Ich habe versucht, einen neuen StringContent mit den drei Parametern zu erstellen, aber es hat nicht funktioniert. Ich habe dann manuell: request.Content.Headers.Remove ("Inhaltstyp") und dann: request.Content.Headers.Add ("Inhaltstyp", "application / query + json") und es hat funktioniert. Seltsam.
Bill Noel
163

Für diejenigen, die Johns Kommentar zur Carlos-Lösung nicht gesehen haben ...

req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
archgl
quelle
Es machte einen Unterschied, ein PDF herunterzuladen. Vom Telefon wurde versucht, einen HTML-Code herunterzuladen. Nach dem Konvertieren der Erweiterung wurde die Datei normalerweise codiert.
Matteo Defanti
Ich musste am Ende .ToString () werfen, aber ja, das funktionierte für eine WCF-Service-Implementierung.
John Meyer
2
Ich werde irgendwann herausfinden, was der Objekttyp "req" ist ... durch Versuch und Irrtum ........ ABER es wäre großartig, das zu zeigen. Danke für deine Rücksicht.
GranadaCoder
4
Nur damit die Leute wissen, dass die Verwendung von MediaTypeHeaderValue einen Fehler zurückgibt, wenn versucht wird, den Zeichensatz wie folgt festzulegen. response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml; charset=utf-8");
MBak
3
Johns Kommentar zu Carlos Lösung sagte Response.Content.Headers, aber Sie verwenden req.Content.Headers? dh Anfrage gegen Antwort?
Joedotnot
52

Wenn Ihnen eine kleine Bibliotheksabhängigkeit nichts ausmacht, macht Flurl.Http [Offenlegung: Ich bin der Autor] dies sehr einfach. Die PostJsonAsyncMethode kümmert sich sowohl um die Serialisierung des Inhalts als auch um das Festlegen des content-typeHeaders und ReceiveJsondeserialisiert die Antwort. Wenn der acceptHeader benötigt wird, müssen Sie dies selbst festlegen, aber Flurl bietet auch eine ziemlich saubere Möglichkeit, dies zu tun:

using Flurl.Http;

var result = await "http://example.com/"
    .WithHeader("Accept", "application/json")
    .PostJsonAsync(new { ... })
    .ReceiveJson<TResult>();

Flurl verwendet HttpClient und Json.NET unter der Haube und ist eine PCL, sodass es auf einer Vielzahl von Plattformen funktioniert.

PM> Install-Package Flurl.Http
Todd Menier
quelle
Wie sende ich, wenn der Inhalt application / x-www-form-urlencoded ist?
Vlado Pandžić
2
Benutzte es. Erreicht in <1 Minute, was ich lange gebraucht habe, um herauszufinden. Vielen Dank, dass Sie diese Bibliothek frei halten.
Najeeb
35

Versuchen Sie, TryAddWithoutValidation zu verwenden

  var client = new HttpClient();
  client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
SharpCoder
quelle
4
Nicht funktioniert gibt mir einen 'Missbrauchten Headernamen. Stellen Sie sicher, dass Anforderungsheader mit HttpRequestMessage, Antwortheader mit HttpResponseMessage und Inhaltsheader mit HttpContent-Objekten verwendet werden. '
Martin Lietz
3
Für diejenigen unter Ihnen, die "arbeiten" oder "nicht arbeiten" melden, ist HttpClient heutzutage ein sehr zweideutiges Objekt. Bitte geben Sie den vollständigen Namen (Leerzeichen) und die DLL-Assembly an, von der sie stammt.
GranadaCoder
Der Misused header nameFehler wird mit dotnet core 2.2 bestätigt. Ich musste @ carlosfigueiras Antwort stackoverflow.com/a/10679340/2084315 verwenden .
ps2goat
es funktioniert für volle .net funktioniert (4.7).
ZakiMa
28

.Net versucht , Sie zu zwingen , bestimmte Standards zu folgen, nämlich , dass der Content-TypeHeader kann nur auf Anfragen angegeben werden, den Inhalt (zB POST, PUTusw.). Daher ist, wie andere angegeben haben, die bevorzugte Methode zum Festlegen des Content-TypeHeaders die HttpContent.Headers.ContentTypeEigenschaft.

Für bestimmte APIs (z. B. die LiquidFiles- API vom 19.12.2016) muss der Content-TypeHeader für eine GETAnforderung festgelegt werden. .Net erlaubt es nicht, diesen Header für die Anfrage selbst zu setzen - auch nicht mit TryAddWithoutValidation. Darüber hinaus können Sie Contentfür die Anforderung kein a angeben, auch wenn diese keine Länge hat. Die einzige Möglichkeit, dies zu umgehen, bestand darin, auf Reflexion zurückzugreifen. Der Code (falls es jemand anderes braucht) ist

var field = typeof(System.Net.Http.Headers.HttpRequestHeaders)
    .GetField("invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static) 
  ?? typeof(System.Net.Http.Headers.HttpRequestHeaders) 
    .GetField("s_invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
if (field != null)
{
  var invalidFields = (HashSet<string>)field.GetValue(null);
  invalidFields.Remove("Content-Type");
}
_client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "text/xml");

Bearbeiten:

Wie in den Kommentaren erwähnt, hat dieses Feld in verschiedenen Versionen der DLL unterschiedliche Namen. Im Quellcode von GitHub wird das Feld derzeit benannt s_invalidHeaders. Das Beispiel wurde geändert, um dies gemäß dem Vorschlag von @David Thompson zu berücksichtigen.

erdomke
quelle
1
Dieses Feld ist jetzt s_invalidHeaders. Wenn Sie also Folgendes verwenden, wird die Kompatibilität sichergestellt: var field = typeof (System.Net.Http.Headers.HttpRequestHeaders) .GetField ("invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static) ?? typeof (System.Net.Http.Headers.HttpRequestHeaders) .GetField ("s_invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
David Thompson
2
Danke danke danke! Manchmal hängt mein Mount offen und Sabber kommt heraus, wenn ich auf einen Microsoft API-Fehler stoße. Ich konnte ihn bereinigen, nachdem ich Ihren sehr einfachen Beitrag gesehen habe. Nicht zu schlecht ..
Gerard ONeill
1
Ich bin verwirrt darüber, wie dieser Code die von Ihnen beschriebenen katastrophalen Fehler verursachen würde. Können Sie weitere Details zu Ihrem Anwendungsfall und den Fehlern, die Sie erhalten, angeben?
Erdomke
2
Beeindruckend. Noch wow, dass Asp.net WebApi GET-Methoden erfordern, dass der Inhaltstyp explizit angegeben wird = (
AlfeG
2
Holly Molly, ich kann nicht glauben, dass ich darauf zurückgreifen muss. Seit wann müssen die .NET Framework-Entwickler meine Hand in dem halten, was ich zum HTTP-Header-Bereich hinzufügen kann? Abscheulich.
mmix
17

Einige zusätzliche Informationen zu .NET Core (nachdem Sie den Beitrag von erdomke zum Festlegen eines privaten Felds gelesen haben, um den Inhaltstyp für eine Anforderung ohne Inhalt anzugeben) ...

Nach dem Debuggen meines Codes kann ich das private Feld, das durch Reflektion festgelegt werden soll, nicht sehen. Daher dachte ich, ich würde versuchen, das Problem neu zu erstellen.

Ich habe den folgenden Code mit .Net 4.6 ausprobiert:

HttpRequestMessage httpRequest = new HttpRequestMessage(HttpMethod.Get, @"myUrl");
httpRequest.Content = new StringContent(string.Empty, Encoding.UTF8, "application/json");

HttpClient client = new HttpClient();
Task<HttpResponseMessage> response =  client.SendAsync(httpRequest);  //I know I should have used async/await here!
var result = response.Result;

Und wie erwartet bekomme ich eine aggregierte Ausnahme mit dem Inhalt "Cannot send a content-body with this verb-type."

Wenn ich jedoch dasselbe mit .NET Core (1.1) mache - mache, bekomme ich keine Ausnahme. Meine Anfrage wurde von meiner Serveranwendung sehr gerne beantwortet und der Inhaltstyp wurde aufgegriffen.

Ich war angenehm überrascht und hoffe, dass es jemandem hilft!

Jay
quelle
1
Danke, Jay - Ich benutze keinen Kern und werde die Antwort von erdomke verwenden. Ich schätze es zu wissen, dass alle vernünftigen Wege ausprobiert wurden :).
Gerard ONeill
1
funktioniert nicht .net 4 ({"Kann keinen Inhaltskörper mit diesem Verbtyp senden."})
Tarek El-Mallah
3
@ TarekEl-Mallah Ja - bitte lesen Sie die Kommentare in meiner Antwort. Der springende Punkt meines Beitrags war zu veranschaulichen, dass es in .NET 4 nicht funktioniert, aber in .NET Core (sie sind nicht dasselbe). Sie müssen die Antwort von erdomeke auf die Frage des OP sehen, um sie hacken zu können, damit sie in .NET 4 funktioniert.
Jay
16

Anruf AddWithoutValidationstatt Add(siehe diesen MSDN-Link ).

Alternativ denke ich, dass die API, die Sie verwenden, dies wirklich nur für POST- oder PUT-Anforderungen erfordert (keine gewöhnlichen GET-Anforderungen). Wenn Sie in diesem Fall ein aufrufen HttpClient.PostAsyncund übergeben HttpContent, legen Sie dies für die HeadersEigenschaft dieses HttpContentObjekts fest.

Robert Levy
quelle
Nicht funktioniert gibt mir einen 'Missbrauchten Headernamen. Stellen Sie sicher, dass Anforderungsheader mit HttpRequestMessage, Antwortheader mit HttpResponseMessage und Inhaltsheader mit HttpContent-Objekten verwendet werden. '
Martin Lietz
3
AddWithoutValidation existiert nicht
KansaiRobot
14

Für diejenigen, die sich Sorgen machen charset

Ich hatte einen ganz besonderen Fall, dass der Dienstanbieter keinen Zeichensatz akzeptierte und sich weigerte, die Unterstruktur zu ändern, um dies zuzulassen ... Leider setzte HttpClient den Header automatisch über StringContent, und egal, ob Sie null oder Encoding.UTF8 übergeben, es wird immer der Zeichensatz eingestellt ...

Heute war ich am Rande, das Subsystem zu ändern; Als ich von HttpClient zu etwas anderem überging, kam mir etwas in den Sinn ..., warum nicht die Reflexion verwenden, um den "Zeichensatz" zu leeren? ... Und bevor ich es überhaupt probiere, habe ich mir überlegt, "vielleicht kann ich es nach der Initialisierung ändern", und das hat funktioniert.

So können Sie den genauen Header "application / json" ohne "; charset = utf-8" setzen.

var jsonRequest = JsonSerializeObject(req, options); // Custom function that parse object to string
var stringContent = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
stringContent.Headers.ContentType.CharSet = null;
return stringContent;

Hinweis: Der nullfolgende Wert funktioniert nicht und hängt "; charset = utf-8" an.

return new StringContent(jsonRequest, null, "application/json");

BEARBEITEN

@DesertFoxAZ schlägt vor, dass auch der folgende Code verwendet werden kann und gut funktioniert. (habe es nicht selbst getestet)

stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
deadManN
quelle
1
stringContent.Headers.ContentType = new MediaTypeHeaderValue ("application / json"); funktioniert auch
DesertFoxAZ
4
var content = new JsonContent();
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("charset", "utf-8"));
content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("IEEE754Compatible", "true"));

Es ist alles was Sie brauchen.

Wenn Sie Newtonsoft.Json verwenden, benötigen Sie einen Inhalt als JSON-Zeichenfolge.

public class JsonContent : HttpContent
   {
    private readonly MemoryStream _stream = new MemoryStream();
    ~JsonContent()
    {
        _stream.Dispose();
    }

    public JsonContent(object value)
    {
        Headers.ContentType = new MediaTypeHeaderValue("application/json");
        using (var contexStream = new MemoryStream())
        using (var jw = new JsonTextWriter(new StreamWriter(contexStream)) { Formatting = Formatting.Indented })
        {
            var serializer = new JsonSerializer();
            serializer.Serialize(jw, value);
            jw.Flush();
            contexStream.Position = 0;
            contexStream.WriteTo(_stream);
        }
        _stream.Position = 0;

    }

    private JsonContent(string content)
    {
        Headers.ContentType = new MediaTypeHeaderValue("application/json");
        using (var contexStream = new MemoryStream())
        using (var sw = new StreamWriter(contexStream))
        {
            sw.Write(content);
            sw.Flush();
            contexStream.Position = 0;
            contexStream.WriteTo(_stream);
        }
        _stream.Position = 0;
    }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        return _stream.CopyToAsync(stream);
    }

    protected override bool TryComputeLength(out long length)
    {
        length = _stream.Length;
        return true;
    }

    public static HttpContent FromFile(string filepath)
    {
        var content = File.ReadAllText(filepath);
        return new JsonContent(content);
    }
    public string ToJsonString()
    {
        return Encoding.ASCII.GetString(_stream.GetBuffer(), 0, _stream.GetBuffer().Length).Trim();
    }
}
art24war
quelle
1
Können Sie eine kleine Erklärung geben, was es tut?
Alejandro
2
Die erste Zeile schlägt mit CS0144 fehl: "Instanz der abstrakten Klasse oder Schnittstelle 'HttpContent' kann nicht erstellt werden"
Randall Flagg
1
und dannHttpMessageHandler handler = new WebRequestHandler(); HttpResponseMessage result; using (var client = (new HttpClient(handler, true))) { result = client.PostAsync(fullUri, content).Result; }
art24war
2

Ok, es ist kein HTTPClient, aber wenn Sie es verwenden können, ist WebClient ganz einfach:

using (var client = new System.Net.WebClient())
 {
    client.Headers.Add("Accept", "application/json");
    client.Headers.Add("Content-Type", "application/json; charset=utf-8");
    client.DownloadString(...);
 }
Ziba Leah
quelle
1

Sie können dies verwenden, es wird Arbeit sein!

HttpRequestMessage msg = new HttpRequestMessage(HttpMethod.Get,"URL");
msg.Content = new StringContent(string.Empty, Encoding.UTF8, "application/json");

HttpResponseMessage response = await _httpClient.SendAsync(msg);
response.EnsureSuccessStatusCode();

string json = await response.Content.ReadAsStringAsync();
Kumaran
quelle
0

Ich finde es am einfachsten und am leichtesten zu verstehen:

async Task SendPostRequest()
{
    HttpClient client = new HttpClient();
    var requestContent = new StringContent(<content>);
    requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    var response = await client.PostAsync(<url>, requestContent);
    var responseString = await response.Content.ReadAsStringAsync();
}
...

SendPostRequest().Wait();
Anshuman Goel
quelle
0

Sie müssen es so machen:

    HttpContent httpContent = new StringContent(@"{ the json string }");
    httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));                
    HttpResponseMessage message = client.PostAsync(@"{url}", httpContent).Result;
user890332
quelle