I am figuring out the new HttpClient.
I try to POST a http uri like this:
http://server/API/user/login?login=name&password=password
I thought this would be the way to go:
using (var client = new HttpClient())
{
var values = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("login", "user"),
new KeyValuePair<string, string>("password ", "password")
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("http://server/REST/user/login", content);
}
The only way I can get it to work is:
using (var client = new HttpClient())
{
var response = await client.PostAsync("http://server/REST/user/login?login=user&password=password",null);
}
Is the last way the correct way or am I doing something wrong in the first approach?
The sample shows that it is being passed along in the Cookie header, not the body. If I interpret the API correctly you have to indeed pass username and password in the URL and give the Cookie header the url-encoded data.
Essentially: use your second approach and for all subsequent calls, add this:
client.DefaultRequestHeaders.Add("Cookie", await content.ReadAsStringAsync());
Related
I'm writing a simple dotnet core API, under search controller which like below :
[HttpGet("order")]
public async Task <Order> SearchOrder(string ordername, int siteid) {
return await service.getorder(ordername,siteid)
}
The swagger UI where the path https://devehost/search/order test pretty work, but when I use another client to call this api by below
client = new HttpClient {
BaseAddress = new Uri("https://devehost")
};
var request = new HttpRequestMessage(HttpMethod.Get, "Search/order") {
Content = new FormUrlEncodedContent(
new List<KeyValuePair<string, string>> {
new("ordername", "pizza-1"),
new("siteid", "1"),
})
};
var response = await client.SendAsync(request);
The status code always return bad request. But the postman is work, can I know the problem inside?
Thank you
For a GET request, the parameters should be sent in the querystring, not the request body.
GET - HTTP | MDN
Note: Sending body/payload in a GET request may cause some existing implementations to reject the request — while not prohibited by the specification, the semantics are undefined.
For .NET Core, you can use the Microsoft.AspNetCore.WebUtilities.QueryHelpers class to append the parameters to the URL:
Dictionary<string, string> parameters = new()
{
["ordername"] = "pizza-1",
["siteid"] = "1",
};
string url = QueryHelpers.AppendQueryString("Search/order", parameters);
using var request = new HttpRequestMessage(HttpMethod.Get, url);
using var response = await client.SendAsync(request);
Due to the fact that I need to convert this C# dll into a tlp file to be called from Visual Basic 6, I need avoid using external dependencies. I have used RestSharp to consume a WebAPI by doing the following (working):
using RestSharp;
using Newtonsoft.Json;
..
public string GetToken (string Key, string Password) {
var client = new RestClient (BaseUrl + "auth/GetToken");
var request = new RestRequest (Method.POST);
request.AddHeader ("cache-control", "no-cache");
request.AddHeader ("Content-Type", "application/json");
Dictionary<string, string> data = new Dictionary<string, string> {
{ "APIKey", Key },
{ "APIPassword", Password }
};
var dataJSON = JsonConvert.SerializeObject (data);
request.AddParameter ("undefined", dataJSON, ParameterType.RequestBody);
IRestResponse response = client.Execute (request);
GetTokenResults g = JsonConvert.DeserializeObject<GetTokenResults> (response.Content);
return g.Token;
}
where GetTokenResults was a struct that contained a declaration for the string Token. I want to achieve this same functionality without using RestSharp. Here is my unsuccessful attempt:
using System.Net.Http;
using System.Net.Http.Headers;
..
public async void GetToken (string Key, string Password) {
var client = new HttpClient ( );
client.DefaultRequestHeaders.Accept.Clear ( );
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue ("application/json"));
client.BaseAddress = new Uri (BaseUrl + "auth/GetToken");
Dictionary<string, string> data = new Dictionary<string, string> {
{ "APIKey", Key },
{ "APIPassword", Password }
};
var dataJSON = JsonConvert.SerializeObject (data);
var content = new StringContent (dataJSON, Encoding.UTF8, "application/json");
var response = await client.PostAsync ("", content);
}
I am unclear on how to achieve the same results (send API key and password, return token as string) using HttpClient as I did using RestSharp earlier. Anything that can point me in the right direction is greatly appreciated!
I think you got stung by this issue. In short, the URI in client.BaseAddress needs a slash at the end of it.
However, I wouldn't simply add it, I'd consider doing it a little different. Presumably your BaseUrl already has a trailing slash, given you're appending "auth/GetToken" to it. I'd do it this way:
client.BaseAddress = new Uri(BaseUrl);
...
var response = await client.PostAsync("auth/GetToken", content);
As you can see, HttpClient fits very cleanly with how your code is already set up, i.e. you have a "base" address with a trailing slash and you want to append to it for a specific call.
That should get you un-stuck to this point. The next thing you'll need to tackle is deserializing the JSON response so you can get the token out of it. It's similar to how you did it in RestSharp, except that response.Content is not a string in the HttpClient world, so you need one more step to get that:
var json = await response.Content.ReadAsStringAsync();
GetTokenResults g = JsonConvert.DeserializeObject<GetTokenResults>(json);
return g.Token;
Last thing you'll need to do to get this to compile is change the method signature to:
public async Task<string> GetTokenAsync
One final note: you are now in the async world, and that's a good thing, but you need to know how to use it correctly or you could end up with deadlocks and other mysterious bugs. In short, don't block on async code by calling .Result or .Wait() anywhere up the call stack. That's by far most common mistake people make. Use async/await all the way down.
I think you are missing first parameter in the method PostAsync i.e. requestUri=Client.BaseAddress (see my implementation below).
Try with this first, if did not work, read below. I have a little different implementation where I passed client.BaseAddress as first parameter and I am passing my content as ByteArrayContent. In my case I have to pass my content as "application/x-www-form-urlencoded" excerpt of my code:
var buffer = Encoding.UTF8.GetBytes(content);
var byteContent = new ByteArrayContent(buffer);
//as I can't send JSON, probably, you can skip as it's already JSON
byteContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
//requestUri=client.BaseAddress
await client.PostAsync(requestUri, byteContent).ConfigureAwait(false);
We have somewhat different need but I think you are pretty close. If it does not help, write me I will share my code. After reading the comment, I would like to share how I have made my HttpClient. The code is as it is:
using (var client = CreateMailClientForPOST($"{BaseUrl}/"))
{
//removed code, you can call above code as method like
var response= await client.DoThingAsAsync($"{client.BaseAddress}, content").ConfigureAwait(false);
}
protected HttpClient CreateMailClientForPOST(string resource)
{
var handler = new HttpClientHandler();
if (handler.SupportsAutomaticDecompression)
{
handler.AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate;
}
var client = new HttpClient(handler)
{
BaseAddress = new Uri($"https://api.address.com/rest/{resource}")
};
return client;
}
Everything works fine on Postman with x-www-from-urlencoded and basic auth. Now trying to get my hands dirty, I just get status code 200 with nothing on mailgun, no logs recorded.
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://api.mailgun.net/v3");
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("api", "key-withheld-till-a-verdict-has-passed");
var msg = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("from",
$"Excited User <mailgun#sandboxSOMEGUIDHERE.mailgun.org>"),
new KeyValuePair<string, string>("to","approved-to-receive#mailgun"),
new KeyValuePair<string, string>("subject", "Test Please Do Not Reply"),
new KeyValuePair<string, string>("text","Thanks for borrowing me your inbox")
};
var request = new HttpRequestMessage(HttpMethod.Post,
"sandboxSOMEGUIDHERE.mailgun.org/messages");
request.Content = new FormUrlEncodedContent(msg);
var response = await client.SendAsync(request);
// I get 200 status code
var result = await response.Content.ReadAsStringAsync();
//I get result = "Mailgun Magnificent API"
}
First, it turns out I was getting the BaseAddress not right. I had to place a slash at the end of the BaseAddress.
client.BaseAddress = new Uri("https://api.mailgun.net/v3/");
without the slash, I was posting to (note v3 is missing),
https://api.mailgun.net/sandboxSOMEGUIDHERE.mailgun.org/messages
After sorting that out, another problem emerged 401 - ANAUTHORIZED. And with the help of this SO answer I do,
var byteArray = new UTF8Encoding()
.GetBytes("api:key-withheld-till-a-verdict-has-passed");
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
But the reason why mailgun was responding with Ok still remain mysterious. To investigate further, I used Postman to post to,
https://api.mailgun.net/sandboxSOMEGUIDHERE.mailgun.org/messages
and to my surprise, Mailgun mysteriously responded with Ok.
I'm trying to send a post variable to the url which I'm redirecting to.
I'm currently using the Get method and sending it like this:
// Redirect to page with url parameter (GET)
Response.Redirect("web pages/livestream.aspx?address="+ ((Hardwarerecorders.Device)devices[arrayIndex]).streamAddress);
And retrieving it like this:
// Get the url parameter here
string address = Request.QueryString["address"];
How do I convert my code to use the POST method?
B.T.W., I don't want to use a form to send the post variable.
Using HttpClient:
To send POST query:
using System.Net.Http;
public string sendPostRequest(string URI, dynamic content)
{
var client = new HttpClient();
client.BaseAddress = new Uri("http://yourBaseAddress");
var valuesAsJson = JsonConvert.SerializeObject(content);
HttpContent contentPost = new StringContent(valuesAsJson, Encoding.UTF8, "application/json");
var result = client.PostAsync(URI, contentPost).Result;
return result.Content.ReadAsStringAsync().Result;
}
Where 'client.PostAsync(URI, contentPost)' is where the content is being sent to the other website.
On the other website, an API Controller needs to be established to receive the result, something like this:
[HttpPost]
[Route("yourURI")]
public void receivePost([FromBody]dynamic myObject)
{
//..
}
However, you might also want to look into using a 307 re-direct, especially if this is a temporary solution.
https://softwareengineering.stackexchange.com/a/99966
using System.Net.Http;
POST
using (var client = new HttpClient())
{
var values = new Dictionary<string, string>
{
{ "thing1", "hello" },
{ "thing2", "world" }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("http://www.example.com/recepticle.aspx", content);
var responseString = await response.Content.ReadAsStringAsync();
}
GET
using (var client = new HttpClient())
{
var responseString = client.GetStringAsync("http://www.example.com/recepticle.aspx");
}
My Personal Choice is Restsharp it's fast but for basic operations you can use this
Before everyone tells me to increase the timeout values I don't want to increase it because it doesn't make sense that 100 seconds isn't long enough. All other get and post responses take less time without throwing a timeout/task cancel exception.
I am trying to mimic the following, I whited out the user and password...
Here is the code
var baseAddress = new Uri("http://www.foodservice.com/home/fs_login.cfm?logmode=Home");
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
var trypage = await client.GetAsync("http://www.foodservice.com/home/fs_login.cfm?logmode=Home");
trypage.EnsureSuccessStatusCode();
Console.WriteLine("something");
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("user_name", "someuser"),
new KeyValuePair<string, string>("password", "somepass"),
new KeyValuePair<string, string>("logmode", "Home")
});
var result = await client.PostAsync("http://www.foodservice.com/home/fs_login_check.cfm", content);
Console.WriteLine("somethingelse");
result.EnsureSuccessStatusCode();
It hands on. something prints, somethingelse doesn't
var result = await client.PostAsync("http://www.foodservice.com/home/fs_login_check.cfm", content);
I don't see any reason why it should hang and fail here...
Summary: Why isn't my post getting through? I am I using the wrong address or something?
Try this
await client.GetAsync("http://www.foodservice.com/home/fs_login.cfm?logmode=Home").ConfigureAwait(false);
and
await client.PostAsync("http://www.foodservice.com/home/fs_login_check.cfm", content).ConfigureAwait(false);
you can get more information on Httpclient Async call
For some reason the process could not be multithreaded.
Switched from Tasks to blocking single threaded it worked fine...