HttpClient.GetStreamAsync() with custom request? - c#

My goal is to use the HttpClient class to make a web-request so that I can write the response to a file (after parsing). Therefore I need the result as a Stream.
HttpClient.GetStreamAsync() only takes the string requestUri as parameter. So there is no possibility to create a request with custom HttpRequestHeader, custom HttpMethod, custom ContentType, custom content and so on?
I saw that HttpWebRequest is sometimes used instead, but in my PCL (Profile111) there is no Add method for the Headers. So can I use HttpClient, should I use HttpWebRequest instead or should I use another class/library at all?

GetStreamAsync is just a shortcut for building and sending a content-less GET request. Doing it the "long way" is fairly straightforward:
var request = new HttpRequestMessage(HttpMethod.???, uri);
// add Content, Headers, etc to request
request.Content = new StringContent(yourJsonString, System.Text.Encoding.UTF8, "application/json");
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
var stream = await response.Content.ReadAsStreamAsync();

Since you mentioned being open to using a different library, here's an alternative that uses Flurl (disclaimer: I'm the author). Say you want to POST some JSON data with a couple custom headers and receive a stream:
var stream = await "https://api.com"
.WithHeaders(new { header1 = "value1", header2 = "value2" })
.PostJsonAsync(data)
.ReceiveStream();
Here data is just a POCO. Don't worry about serializing it to a JSON string or setting Content-Type to application/json; Flurl will do both for you.
Flurl uses HttpClient under the hood and targets .NET Standard 1.1, which is fully compatible with PCL Profile111.

Related

How do I send a post request to telegram API from C#?

I have my telegram application with app's api_id and app's api_hash.
I used TLSharp library for implementing my own things. But now I need to use this https://core.telegram.org/method/auth.checkPhone telegram api method, but it's not implemented in TLSharp library!
I don't mind doing it all manually, but I don't know how!
I know how you send post requests in C#, example:
var response = await client.PostAsync("http://www.example.com/index", content);
but in this specific case I don't. Because I don't know:
1) what link should I use for sending post requests? I couldn't find it on the telegram's website.
2) what content should I pass there? Should it be just "(auth.checkPhone "+380666454343")" or maybe the whole "(auth.checkPhone "+380666454343")=(auth.checkedPhonephone_registered:(boolFalse)phone_invited:(boolFalse))" ?
So, How do I sent this post request to the telegram api? (NOT telegram bot api!)
Try to use System.Net.Http like in this example (auth request to the server):
var user = new { login = "your login", password = "your pass" };
string json = JsonConvert.SerializeObject(user);
HttpContent content = new StringContent(json, Encoding.UTF8, "application/json");
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
request.RequestUri = new Uri("server route link"); // can be like https://a100.technovik.ru:1000/api/auth/authenticate
request.Method = HttpMethod.Post;
request.Content = content;
HttpResponseMessage response = await client.SendAsync(request);
responseText.Text = await response.Content.ReadAsStringAsync();
I think based on a brief look, that it would be more along the lines of your second example, e.g.:
var phonenumber = "0123456789";
var content =
$#"(auth.checkPhone ""{phonenumber}"")"+
"=(auth.checkedPhone phone_registered: (boolFalse) phone_invited:(boolFalse))";
var result = DoHttpPost("http://some.example.com/api/etc", content);
(note: I've not listed the actual mechanics of an HTTP Request here, as that is covered in plenty of detail elsewhere - not least in the other current answer supplied to you; DoHttpPost() is not a real method and exists here only as a placeholder for this process)
And since the payload of this appears to indicate the exact function and parameters required, that you'd just send it to the base api endpoint you use for everything, but I can't say for sure...
I do note they do appear to have links to source code for various apps on the site though, so perhaps you'd be better off looking there?

Call HTTP GET with JSON body content parameters

How can I call HTTP GET using JSON parameters in content body?
I tried this:
HttpWebRequest.WebRequest.Create(_uri);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "GET";
httpWebRequest.Headers.Add("X-AUTH-TOKEN", _apiKey);
using(var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream())) {
string _json = "\"{\"filter\": {\"relation\": \"equals\", \"attribute\": \"state\", \"value\": \"CA\" }, \"insights\": {\"field\": \"family.behaviors\", \"calculations\": [\"fill_count\"]}}";
streamWriter.Write(_json);
streamWriter.Flush();
streamWriter.Close();
}
var httpResponse = (HttpWebResponse) httpWebRequest.GetResponse();
using(var streamReader = new StreamReader(httpResponse.GetResponseStream())) {
var result = streamReader.ReadToEnd();
}
but it throws an exception:
"Cannot send a content-body with this verb-type."
If you use .NET core, the new HttpClient can handle this. Otherwise you can use System.Net.Http.WinHttpHandler package, but it has a ton of dependencies. See answer
https://stackoverflow.com/a/47902348/1030010
for how to use these two.
I can't use .NET core and I don't want to install System.Net.Http.WinHttpHandler.
I solved it by using reflection, to trick WebRequest that it is legal to send body with a GET request (which is according to latest RFC). What I do is to set ContentBodyNotAllowed to false for HTTP verb "GET".
var request = WebRequest.Create(requestUri);
request.ContentType = "application/json";
request.Method = "GET";
var type = request.GetType();
var currentMethod = type.GetProperty("CurrentMethod", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(request);
var methodType = currentMethod.GetType();
methodType.GetField("ContentBodyNotAllowed", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(currentMethod, false);
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
streamWriter.Write("<Json string here>");
}
var response = (HttpWebResponse)request.GetResponse();
Note, however, that the attribute ContentBodyNotAllowed belongs to a static field, so when its value changes, it remains in effect for the rest of the program. That's not a problem for my purposes.
It is entirely possible, but you have to use the newer HttpClient class: https://stackoverflow.com/a/47902348/70345
Even tho it is technically allowed to send a body with Get requests, Microsoft has decided for you that you cannot do that.
This can be seen in HttpWebRequest source code:
if (onRequestStream) {
// prevent someone from getting a request stream, if the protocol verb/method doesn't support it
throw new ProtocolViolationException(SR.GetString(SR.net_nouploadonget));
}
So you need to change your verb to Put or Post or have some other workaround.
GET will only receive it.
If you need to specify parameters, please include it in url.
Or you can send JSON BODY if POST or PUT.
HTTP request methods
HTTP defines a set of request methods to indicate the desired action to be performed for a given resource. Although they can also be nouns, these request methods are sometimes referred as HTTP verbs. Each of them implements a different semantic, but some common features are shared by a group of them: e.g. a request method can be safe, idempotent, or cacheable.
GET
The GET method requests a representation of the specified resource. Requests using GET should only retrieve data.
HEAD
The HEAD method asks for a response identical to that of a GET request, but without the response body.
POST
The POST method is used to submit an entity to the specified resource, often causing a change in state or side effects on the server.
PUT
The PUT method replaces all current representations of the target resource with the request payload.
In Addition:
I found this. Long discussion has been held.
HTTP GET with request body
What this means is that it is possible to send BODY with GET, but sending a payload body on a GET request might cause some existing implementations to reject the request (such as Proxy in the middle of the route).
Please be sure to read this article carefully as there are many other points to pay attention to.
By the way, it seems that you can send GET with body using the -i option of cURL command.
Curl GET request with json parameter

StringContent vs ObjectContent

I am using System.Net.Http's HttpClient to call a REST API with "POST"
using the following code:
using (HttpRequestMessage requestMessage = new HttpRequestMessage(
HttpMethod.Post, new Uri(request)) { })
{
response = await httpClient.PostAsync(request, objectContent);
}
The "objectContent" is currently this -
objectContent = new ObjectContent(jsonContent.GetType(),
jsonContent,
new JsonMediaTypeFormatter());
I was wondering what difference it makes if this was a StringContent rather than an ObjectContent like this?
objectContent = new StringContent(content);
objectContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
Both work fine. Because it is JSON, i tend to assume that StringContent would make sense. But when is ObjectContent to be used because pretty much all content sent is a "string".
I was wondering what difference it makes if this was a StringContent
rather than an ObjectContent like this?
In your example there won't be any difference. ObjectContent simply allows a "wider" range of types to be sent via HttpClient, while StringContent is narrower for string values only, such as JSON.
StringContent is a slim wrapper around ByteArrayContent, and actually stores the value passed as a byte[]. You simply get the benefit of not needing to transform your string back and forth.
Edit:
Given the fact that you're posting a JSON, you can even make it less verbose by using HttpClientExtensions.PostAsJsonAsync<T>:
await httpClient.PostAsJsonAsync(url, json);
If someone will search how to send request by PostAsync in .NET Core 2.1:
I did not found PostAsJsonAsync method in HttpClient, but your solution with setting:
objectContent = new StringContent(content);
objectContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
"do the job" perfectly also in .NET Core.
Edit:
Additionally If you want add your own header you can type:
objectContent.Headers.Add("Your header", "Value");
ObjectContent is used to format more complex Mime types using built-in or custom-written formatters. It is OK to use ObjectContent for a simple string as well, it doesn't make much difference except for the performance which (IMHO and not checked) may be better with StringContent, since it may have been optimized specifically for strings

Set HTTP protocol version in HttpClient

I need to make a request to a webservice that uses HTTP version 1.0. Im using HttpClient , But I cant see any option to set HTTP version.
Where can i set the request version?
In order to set the version you'll have to create an instance of HttpRequestMessage and set its Version property which you pass to HttpClient.SendAsync. You can use the helper HttpVersion utility class:
var requestMessage = new HttpRequestMessage
{
Version = HttpVersion.Version10
};
var client = new HttpClient();
var response = await client.SendAsync(requestMessage);
HTTP version is sent as a header in every request, so it is set in the message sent by System.Net.Http.HttpClient: see the ProtocolVersion property of the HttpWebRequest class.

Creating and sending an HttpResponse message

Given either a url or a HttpResponse instance, I need to send back a HttpResponse (note Response not Request) with Status code 200. I've tried a generic HttpClient.PostAsync and GetAsync, but I can't configure the status code.
This is basically a handshake for subscribing to events for a remote service. I send a Request, get an OK back from the server and now it's expecting me to reply to the OK with my own OK.
Any ideas on how to go about this? I don't have an incoming request to respond to.
var resp = new HttpResponseMessage(HttpStatusCode.OK);
// need to sent this to the url. no content necessary.
Here's some things I cannot do:
var client = new HttpClient();
var resp = await client.GetAsync(url);
-------------------
var content = new ByteArrayContent(new byte[0]);
var client = new HttpClient();
var resp = await client.PostAsync(url, content);
When you invoke HttpClient calls you are creating HttpRequestMessages. There are a number of helper methods (like some PostAsync overloads or PostAsJsonAsync) which let you pass in a plain old C# object, which it then wraps for you. These values get set in the Content property of the HttpRequestMessage, wrapped by an instance of HttpContent. You're also perfectly free to create these request messages yourself, setting the status code and content to anything you like. HttpResponseMessage (note Response, not Request) is the type you'll get back from your client call, which you can use to read the response code or data sent back to you (stored in HttpResponseMessage.Content, not to be confused with HttpRequestMessage.Content, which you would have already set).
That said, you CAN set your request content to an instance of HttpResponseMessage, but that would be a little bit odd. Generally, the objects you use for your content should be simple objects which exist simply to define the shape of your requests' body (like JSON).
This article goes over the basics pretty well: Calling a Web API from a .Net Client
You should be able to set the status directly on the HttpResponse object
Something like this...
HttpResponse().StatusCode = 200;

Categories

Resources