how to make an OPTIONS request with HttpClient - c#

How do I send an OPTIONS request with System.Net.Http.HttpClient
exposed methods for HttpClient
DeleteAsync
GetAsync
PostAsync
PutAsync
few others as well.....
I was expecting a OptionsAsync
switch (httpMethod) {
case HTTP_METHODS.DELETE:
{
httpResponseMessage = httpClient.DeleteAsync(uri).Result;
break;
}
case HTTP_METHODS.GET:
{
httpResponseMessage = httpClient.GetAsync(uri).Result;
break;
}
case HTTP_METHODS.POST:
{
httpResponseMessage = httpClient.PostAsync(uri, httpContent).Result;
break;
}
case HTTP_METHODS.PUT:
{
httpResponseMessage = httpClient.PutAsync(uri, httpContent).Result;
break;
}
case HTTP_METHODS.OPTION:
{
//not sure what method to call on httpclient here to make Options request
httpResponseMessage = httpClient.PutAsync(uri, httpContent).Result;
if (httpResponseMessage.Headers.Contains("X-CSRF-TOKEN")) {
IEnumerable < string > headerValues = httpResponseMessage.Headers.GetValues("X-CSRF-TOKEN");
csrfToken = headerValues.FirstOrDefault();
}
break;
}
}

There are no wrappers for that kind of methods (e.g. OPTIONS and HEAD), but you could use SendAsync just like these wrappers do:
var request = new HttpRequestMessage(HttpMethod.Options, "url");
var result = await _httpClient.SendAsync(request);

In order to get the expected response back, you have to make sure to set the "Origin" header in the request, like so:
using var client = new HttpClient();
var message = new HttpRequestMessage(HttpMethod.Options, "url");
message.Headers.Add("Origin", "http://example.com");
var response = await client.SendAsync(message);

Related

C# - httpclient - json body - Not getting applied appropriately using httpcontent

The following is the code from where I would return a tuple of response status code and response output.
private Tuple<int, string> API_Check(string URL, string reqtype, string reqbody, string split_username, string split_pwd)
{
string responsetxt="";
HttpResponseMessage httpresult = new HttpResponseMessage();
int statuscode = 0;
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
HttpClient _httpClient = new HttpClient();
var authString = Convert.ToBase64String(Encoding.UTF8.GetBytes(split_username+":" + split_pwd));
_httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authString);
try
{
using (var content = new StringContent(JsonConvert.SerializeObject(reqbody)))
{
if (reqtype == "GET")
{
httpresult = _httpClient.GetAsync(URL).Result;
}
if (reqtype == "PUT")
{
httpresult = _httpClient.PutAsync(URL, content).Result;
//httpresult = _httpClient.PutAsync()
}
if (reqtype == "POST")
{
httpresult = _httpClient.PostAsync(URL, content).Result;
}
statuscode = (int)httpresult.StatusCode;
responsetxt = httpresult.Content.ReadAsStringAsync().Result;
return Tuple.Create(statuscode, responsetxt);
}
}
catch (System.Net.WebException Excptn)
{
statuscode = 401;
responsetxt = Excptn.Status.ToString();
using (var stream = Excptn.Response.GetResponseStream())
using (var reader = new StreamReader(stream))
{
MessageBox.Show(reader.ReadToEnd());
}
}
return Tuple.Create(statuscode, responsetxt);
}
For some reason, the request body is not getting filled correctly during the call. I'm getting 401 Unauthorized as for this Post call, which is definitely not a authorization error as the response message that I receive is equivalent to empty body or invalid input json format.
When I tried to hit the same reqbody for the endpoint with Postman, I'm getting 200 with valid response. Also, the GetAsync works for a similar API which doesn't require a body.
I verified there is no issues with the username, password or the Endpoint URL.
Is there a way, I could avoid using httpcontent and use the string as it is for hitting the API through C#?
Now, I could not use HttpWebRequest due to my current .Net framework limitations.
There are many issues with your code:
Primarily, you are serializing reqbody which is already a string. It sounds like you have a JSON string already, in which case you don't need to serialize it.
Don't use .Result, it can cause a deadlock. use await instead.
Use Valuetuples instead of Tuple, which can be inefficient.
Do not set ServicePointManager.SecurityProtocol = ..., instead let the operating system choose the best security protocol.
Do not use ServicePointManager in general, as it affects all HTTP request from your app. Instead set the relevant HtppClient property, or better: use HttpRequestMessage and set it directly on the message.
You can simplify the code a bit if you use HttpRequestMessage, giving it the type of HTTP method
You are catching the wrong exception type. You should be catching HttpRequestException, from which you can get the actual StatusCode.
HttpClient by default does not throw on non-success codes. You need to handle them explicitly.
Cache the HttpClient, or you could get socket exhaustion.
Creating a new HttpResponseMessage doesn't make a huge amount of sense.
HttpClient _httpClient = new HttpClient {
DefaultRequestHeaders = {
ExpectContinue = false,
},
};
private async Task<(int, string)> API_Check(string URL, HttpMethod reqtype, string reqbody, string split_username, string split_pwd)
{
var authString = Convert.ToBase64String(Encoding.UTF8.GetBytes(split_username + ":" + split_pwd));
try
{
using (var content = new StringContent(reqbody))
using (var request = new HttpRequestMessage(URL, reqtype))
{
message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authString);
if (reqtype != "GET")
message.Content = content;
using var httpresult = await _httpClient.SendAsync(URL, content);
var statuscode = (int)httpresult.StatusCode;
var responsetxt = await httpresult.Content.ReadAsStringAsync();
if (!httpresult.IsSuccessStatusCode)
MessageBox.Show(responsetxt);
return (statuscode, responsetxt);
}
}
catch (HttpRequestException ex)
{
var statuscode = ex.StatusCode ?? 0;
var responsetxt = ex.Message;
MessageBox.Show(responsetxt);
return (statuscode, responsetxt);
}
}
If you actually have an object to serialize then change the method to
private async Task<(int, string)> API_Check(string URL, HttpMethod reqtype, object reqbody, string split_username, string split_pwd)
{
....
....
using (var content = new StringContent(JsonConvert.SerializeObject(reqbody)))

How can I translate Webrequest method in HttpClient

I have this code for recovering Http code and description, but Visual Studio says that WebRequest is obsolete and that I have to use HttpClient. I'm not familiar with HttpClient can you help me?
IConfigurationRoot config;
myRequest = WebRequest.CreateHttp($"{config["BASE_URL"]}{link}");
myResponse = (HttpWebResponse)myRequest.GetResponse();
Check_load($"{config["BASE_URL"]}{link}");
var code = ((int)myResponse.StatusCode);
var desc = myResponse.StatusDescription;
var client = new HttpClient();
var response = await client.GetAsync(url);
var statusCode = response.StatusCode;
var content = response.Content;
if(statusCode != HttpStatusCode.OK && statusCode != HttpStatusCode.Created){
//return something, error, description
}
Update
From comment #Charlieface pointed out where I haven't declared HttpClient as static field. To avoid creating instances of HttpClient every time it is called, the complete workground as below.
public class HttpHelper{
private static HttpClent client = new HttpClient();
public static async Task<string> GetAsync(string url)
{
var response = await client.GetAsync(url);
var statusCode = response.StatusCode;
var content = response.Content;
if(statusCode != HttpStatusCode.OK && statusCode != HttpStatusCode.Created)
{
//return something, error, description
//return $"error with status code: {statusCode}";
}
else
{
//return "success";
}
}
}
The use of HttpClient is very simple. See the code below
try
{
using (HttpClient client = new HttpClient())
{
var res = await client.GetAsync(url);
var response = await res.Content.ReadAsStringAsync();
var data = JsonConvert.DeserializeObject<MyObject>(response);
// use the new data retreived...
}
}
catch (Exception)
{
responseMessage = new BadRequestObjectResult("error retreiving data");
}
This suppose your data are sent in JSON format

C# HttpClient request API suite

I'm trying to develop an API suite so I can call it by passing some parameters. I successfully made the part about GET method.
POST seems a bit more complicated and I didn't find any source so I can understand how to make it. My plan is to call in POST with a body as parameter
I didn't develop the API I'm going to POST but it's not even a public one but inside the project development. Using .NET core 5.0. The scope is to call some APIs as regression tests before any deploy from the dev team and some of them might return a data that will be used as body param in the next API so I'm developing a console app that check if everything is OK.
This is the GET request I developed, could you help me understand how to make it using POST method?:
static void Main(string[] args)
{
string url = ConfigurationManager.AppSettings["url"];
string jsonBody = "";
string method = "GET";
string result = CallApi(url, method, jsonBody);
}
public static string CallApi(string url, string method, string bodyInput)
{
string bodyOutput = null;
HttpClient client = new();
Uri uri = new(url);
switch (method)
{
case "GET":
Task<HttpResponseMessage> response = client.GetAsync(uri);
HttpResponseMessage result = response.Result;
HttpContent content = result.Content;
bodyOutput = content.ReadAsStringAsync().Result;
break;
case "POST":
//bodyInput
break;
case "PUT":
break;
case "DELETE":
break;
default:
break;
}
return bodyOutput;
}
}
Thanks
Hi your code should look like this:
static async Task Main(string[] args)
{
string baseAddress = ConfigurationManager.AppSettings["baseAddress"];
string path = ConfigurationManager.AppSettings["path"];
string jsonBody = "";
HttpMethod method = HttpMethod.Get;
string result = await CallApi(baseAddress, path, method, jsonBody);
}
public static async Task<string> CallApi(string baseAddress, string path, HttpMethod method, string body)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(baseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = null;
switch (method)
{
case HttpMethod.Get:
response = await client.GetAsync(path);
break;
case HttpMethod.Post:
response = await client.PostAsJsonAsync(path, body);
break;
case HttpMethod.Put:
response = await client.PutAsJsonAsync(path, body);
break;
case HttpMethod.Delete:
response = await client.DeleteAsync(path);
break;
default:
throw new NotImplementedException();
}
// Throw an exception if the response code is not successful
response.EnsureSuccessStatusCode();
// Read the response
string bodyOutput = await response.Content.ReadAsStringAsync();
return bodyOutput;
}
}
You can see a more detailed example here:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client
call this function using await , unlike what your doing in your function.
Post :
static async Task<string> Postfunc()
{
var json = Newtonsoft.Json.JsonConvert.SerializeObject(new object obj());
var data = new System.Net.Http.StringContent(json, Encoding.UTF8, "application/json");
var client = new HttpClient();
var response = await client.PostAsync(url, data);
string result = await response.Content.ReadAsStringAsync();
return result;
}

HttpClient post returns bad request in c#, works in postman

I'm trying to access a rest endpoint, https://api.planet.com/auth/v1/experimental/public/users/authenticate. It is expecting json in the request body.
I can get the request to work in Postman but not using c#. Using postman I get the expected invalid email or password message but with my code I get "Bad Request" no matter I try.
Here is the code that makes the request
private void Login()
{
try
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("https://api.planet.com/");
client.DefaultRequestHeaders.Accept.Clear();
//ClientDefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
Data.User user = new Data.User
{
email = "myemail#company.com",
password = "sdosadf"
};
var requestMessage = JsonConvert.SerializeObject(user);
var content = new StringContent(requestMessage, Encoding.UTF8, "application/json");
var response = client.PostAsync("auth/v1/experimental/public/users/authenticate", content).Result;
Console.WriteLine(response.ToString());
}
catch (WebException wex )
{
MessageBox.Show(wex.Message) ;
}
}
class User
{
public string email;
public string password;
}
Here are screen grabs form Postman that are working
The way to get this to work was to alter the content header "content-type". By default HTTPClient was creating content-type: application/json;characterset= UTF8. I dropped and recreated the content header without the characterset section and it worked.
content.Headers.Remove("Content-Type");
content.Headers.Add("Content-Type", "application/json");
The issue is you are trying to call an async method without waiting for the response using await method or var task = method; task.Wait() Therefore, when you end up doing response.ToString() it returns the text you are seeing.
One way to handle this within a non-async method would be to do the following:
var task = client.PostAsync("auth/v1/experimental/public/users/authenticate", content);
task.Wait();
var responseTask = task.Content.ReadAsStringAsync();
responseTask.Wait();
Console.WriteLine(responseTask.Result);
Another way is to make the current method async by doing private async void Login() and then do:
var postResp = await client.PostAsync("auth/v1/experimental/public/users/authenticate", content);
var response = await postResp.Content.ReadAsStringAsync();
Console.WriteLine(response);
Create a Method Like this...
static async Task<string> PostURI(Uri u, HttpContent c)
{
var response = string.Empty;
var msg = "";
using (var client = new HttpClient())
{
HttpResponseMessage result = await client.PostAsync(u, c);
msg = await result.Content.ReadAsStringAsync();
if (result.IsSuccessStatusCode)
{
response = result.StatusCode.ToString();
}
}
return response;
}
call In your Method
public void Login()
{
string postData ="{\"email\":\"your_email\",\"password\":\"your_password\"}";
Uri u = new Uri("yoururl");
var payload = postData;
HttpContent c = new StringContent(payload, Encoding.UTF8,"application/json");
var t = Task.Run(() => PostURI(u, c));
t.Wait();
Response.Write(t.Result);
}

PutAsync doesn't send request to web api, but fiddler works fine

I have been trying to figure out what is going wrong for a few hours now and i just can't find what is going wrong.
Via the Mvc application the put method doesn't get hit, the request doesn't happen. But when i test it in fiddler the PutMethod in the api works.
Hopefully someone can clear things up for me.
Also pointers for a better structure or some good documentation are welcome .
public void UpdateWerknemerCompetentieDetail(int wnID, int WNC, CompetentieWerknemerDetail detail)
{
using (HttpClient client = new HttpClient())
{
string token = (string)HttpContext.Current.Session["token"];
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var wn = GetWerknemerById(wnID);
//var wnc = wn.CompetentiesWerknemer.Select(c => c).Where(c => c.ID == WNC).FirstOrDefault();
detail.CompetentieWerknemerID = WNC;
//wnc.CompetentieWerknemerDetail = detail;
var url = String.Format(URL + "PutDetails?id=" + WNC);
var json = JsonConvert.SerializeObject(detail, new JsonSerializerSettings()
{
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
});
var response = client.PutAsync(url, new StringContent(json, Encoding.UTF8, "application/json"));
}
}
The above code is my service that should make the request to the api.
Here is the web api IHttpActionResult method (the put method).
[Route("PutDetails")]
[HttpPut]
public IHttpActionResult PutWerknemerCompetentieDetails(int id, [FromBody]CompetentieWerknemerDetail cwn)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != cwn.CompetentieWerknemerID)
{
return BadRequest();
}
//_db.Entry(cwn).State = EntityState.Modified;
try
{
_db.CompetentieWerknemerDetail.Add(cwn);
_db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!WerknemerExist(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
HttpClient.PutAsync is an asynchronous API, it returns a Task<HttpResponseMessage> which represents an operation which will complete in the future, which you need to await. You're wrapping your HttpClient inside a using statement, which means that right after you trigger the asynchronous PUT, you're disposing the client which causes a race condition with the request and the disposal of your object, and is probably the reason you're not seeing the request fire.
You have two choices. Either make the method async Task and await inside it:
public async Task UpdateWerknemerCompetentieDetailAsync(
int wnID, int WNC, CompetentieWerknemerDetail detail)
{
using (HttpClient client = new HttpClient())
{
string token = (string)HttpContext.Current.Session["token"];
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
var wn = GetWerknemerById(wnID);
//var wnc = wn.CompetentiesWerknemer.Select(c => c)
// .Where(c => c.ID == WNC)
// .FirstOrDefault();
detail.CompetentieWerknemerID = WNC;
//wnc.CompetentieWerknemerDetail = detail;
var url = String.Format(URL + "PutDetails?id=" + WNC);
var json = JsonConvert.SerializeObject(detail, new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
var response = await client.PutAsync(
url, new StringContent(json, Encoding.UTF8, "application/json"));
}
}
Or use a synchronous API, such as exposed by WebClient.

Categories