SendAsync() returning 422 Unprocessable Entity - c#

I wrote a function using GetAsync() which works fine, but now i'd like to expand on it using SendAsync() instead [for POSTing and sofourth]; however my SendAsync() version is not working as expected, it returns a 422 unprocessible entity. (no IDE atm; sorry for minor typos)
init
var Client = new HttpClient{
BaseAddress = "https://example.com"
}
Client.DefaultRequestHeaders.UserAgent.ParseAdd("Project/1.0 (blah blah)");
...
Working GetAsync()
public async Task<string> GetResponse(string user, string pass){
var uri = $"/user/login.json?name={user}&password={pass}";
var req = await Client.GetAsync(uri);
return req.Content.Request.Content.ReasStringAsync();
}
non working SendAsync()
public async Task<string> GetResponse(string page, Dictionary<string, string> args){
//assume page = "/user/login.json" and args == {"username", "user"},{"password", "pass"}
try{
var req = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(page),
Content = new FormUrlEncodedContent(args),
}
var response = await Client.SendAsync(req);
if(response.IsSuccessStatusCode){
return await response.Content.ReasStringAsync();
return null;
}
catch{ return null }
}
note: along with the 422, the response still contains json which states "invalid Auth Token!"
What is GetAsync() doing that SendAsync() is not?

Your Send included content in the BODY of a HTTP GET request.
HTTP GET requests should not have a BODY and there are servers that wont process such requests.
Convert the dictionary to a QueryString and include it in the URI.
public async Task<string> GetResponse(string page, Dictionary<string, string> args) {
//assume page = "/user/login.json" and args == {"username", "user"},{"password", "pass"}
try {
QueryString queryString = QueryString.Create(args);
var uri = new Uri(page + queryString.ToString());
var request = new HttpRequestMessage(HttpMethod.Get, uri);
var response = await Client.SendAsync(request);
if(response.IsSuccessStatusCode){
return await response.Content.ReadAsStringAsync();
return string.Empty;
} catch { return string.Empty; }
}

Your code snippets don't show it, but are you sure the second query's URL has
$"/user/login.json?name={user}&password={pass}"
and not
$"/user/login.json"
?

Related

Why RestAPI occasionally returns 429 TooManyRequest response?

I send a request to the API and sometimes receive the response with an HTTP 429 status code (TooManyRequests).
On average, for 10 requests, 2 will return 429 response and the remaining 8 return the correct value.
It also happened to me that when it was the first request (so there is no option for TooManyRequests)
public static List<ResponseObject> GetProductsFromRestAPI(int[] ProductIdArray )
{
List<ResponseObject> products = new List<ResponseObject>();
string action;
for (int i = 0; i < ProductIdArray.Length; i++)
{
action = "products/" + ProductIdArray[i].ToString();
client = AddHeadersToClient(action, new RestClient("https://api.usedbythiscode.com/")); //required by this API.
var currentProduct = RequestsLib.GetProduct(client, action);
products.Add(currentProduct);
}
return products;
}
public static Product GetProduct(RestClient restClient, string action) //todo test this for bugs
{
var result = new Product();
var request = new RestRequest(action, Method.GET);
var response = SendRequest(restClient, request);//Here I sometimes get response with 429.
//.. Other code
return result;
}
public static async Task<IRestResponse> SendRequest(RestClient restClient, RestRequest request)
{
return await restClient.ExecuteGetAsync(request);
}
Temporarily resolved it by sending another request with do while loop and usually second request return right answer.
do
{
SendRequest(restClient, request);
}
while (StatusCode != 200);
Where could the cause of the error lie?
Is it possible that I have unclosed requests?
Is creating multiple RestSharp clients a good practice?
EDIT:
The problem was on the server side. All I had to do was report the bug to the admins who provided the API. Thank you for help.
429 is Too Many Requests. Most APIs have some kind of rate-limiting in place so that a single client can't take down their server with too many requests.
The proper response for 429 is to retry. I recommend using Polly for retry logic, but be aware that HandleTransientHttpError doesn't consider 429 a transient error.
I agree with #mason, you should use async method with Task<> and await response Here is the part of login side of my mobileApp-project in Xamarin. You may want to see how to use async with Task<> easily.
public async Task<BSUser> ValidateUser(string userName, string password)
{
string url = Xamarin.Essentials.Preferences.Get(Constants.URL_KEY, "") + "/api/Validateuser";
HttpClient _Client = new HttpClient();
var data = new Dictionary<string, string>
{
{"userName", userName},
{"password", password}
};
string jsonData = JsonConvert.SerializeObject(data);
HttpContent content = new StringContent(jsonData, Encoding.UTF8, "application/json");
try
{
HttpResponseMessage httpResponse = await _Client.PostAsync(url, content);
if (httpResponse.IsSuccessStatusCode)
{
try {
var responseData = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
var result = JsonConvert.DeserializeObject(responseData).ToString();
UserInfo userInfo = JsonConvert.DeserializeObject<UserInfo>(result);
BSUser value = new BSUser();
value.UserName = userInfo.userCode;
return value;
}
catch (Java.Net.SocketException e)
{
Console.WriteLine("Hata", e);
return null;
}
}
else
{
return null;
}
}
catch (SystemException)
{
return null;
}
}

Prepare controller and http request to preocess request without returning value

I have following method to get collection of cars:
public async Task<IEnumerable<Car>> GetCars(object car, CancellationToken ct)
{
var uri = new Uri($"{_webApiUrl}Car/GetCars");
using (var httpClient = new HttpClient())
{
HttpContent content = new StringContent(JsonConvert.SerializeObject(car), Encoding.UTF8, "application/json");
var req = await httpClient.PostAsync(uri, content, ct);
var response = await req.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<IEnumerable<Car>>(response);
}
}
In my service api in controller i have this to get it:
[HttpPost]
[Route("api/[controller]/[action]")]
public IActionResult GetBottleTypeById(Car car)
{
return Ok(_carQuery.GetCars(car));
}
Both things works great - no issues.
Now i need to save the Car and i don't need to return something (eventually i could return true or false whether it was succesfull or not). The problem is i am not sure how controller's method should look like, if i do this way it says: not all code path returns value:
[HttpPost]
[Route("api/[controller]/[action]")]
public IActionResult UpdateOperation(Car car)
{
_carRepository.UpdateOperation(car) //UpdateOperation is void with try/catch - throw;
}
Same thing with request, how this should be accomplished?
public async Task UpdateCar(Car selectedCar, CancellationToken ct)
{
var uri = new Uri($"{_webApiUrl}Car/UpdateOperation");
using (var httpClient = new HttpClient())
{
HttpContent content = new StringContent(JsonConvert.SerializeObject(selectedCar), Encoding.UTF8, "application/json");
var req = await httpClient.PostAsync(uri, content, ct);
var response = await req.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<IEnumerable<Car>>(response);
}
}
How could i prepare both things to make it work?
Just change your code to return void. ASP.NET is smart enough to just send 200 OK if no exceptions were thrown out of a void method.
[HttpPost]
[Route("api/[controller]/[action]")]
public void UpdateOperation(Car car)
{
_carRepository.UpdateOperation(car) //UpdateOperation is void with try/catch - throw;
}
To make your calling code work, you don't need to receive anything but the HTTP response message from the server. Even if your method returns void, HTTP servers still send a status code (200, 500, etc.) and some headers for you to inspect.
Try this:
public async Task UpdateCar(Car selectedCar, CancellationToken ct)
{
var uri = new Uri($"{_webApiUrl}Car/UpdateOperation");
using (var httpClient = new HttpClient())
{
HttpContent content = new StringContent(JsonConvert.SerializeObject(selectedCar), Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(uri, content, ct);
if (response.IsSuccessStatusCode)
{
return;
}
else
{
// handle error
}
}
}

How can I send a POST request to a remote server?

My website needs to post requests to a remote server to use its API.
I found a way in google by using HttpClient, just like this:
public async Task<string> HttpPostAsync(string uri, string url, List<KeyValuePair<string, string>> formData = null, string charset = "UTF-8", string mediaType = "application/x-www-form-urlencoded")
{
string tokenUri = url;
var client = new HttpClient();
client.BaseAddress = new Uri(uri);
HttpContent content = new FormUrlEncodedContent(formData);
content.Headers.ContentType = new MediaTypeHeaderValue(mediaType);
content.Headers.ContentType.CharSet = charset;
for (int i = 0; i < formData.Count; i++)
{
content.Headers.Add(formData[i].Key, formData[i].Value);
}
HttpResponseMessage resp = await client.PostAsync(tokenUri, content);
resp.EnsureSuccessStatusCode();
string token = await resp.Content.ReadAsStringAsync();
return token;
}
This way works, but the example is from 2017 when .NET Core 1.1 was just published.
Now .NET Core 3.0 is available and I wonder if there is any other better way to achieve this?
HttpClient is still the simplest/best way to directly make HTTP calls in .NET. For a method that always posts form content, you can simplify it a bit (no need to specify the media type, charset, etc. every time):
public static async Task<string> Post(string url, IEnumerable<KeyValuePair<string, string>> formData)
{
var client = new HttpClient();
var formContent = new FormUrlEncodedContent(formData);
var response = await client.PostAsync(url, formContent);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
var stringResponse = await Post("https://so57994582.free.beeceptor.com",
new Dictionary<string, string>() { { "hello", "world" } });

Response content of HttpResponseMessage on making a Post request is always '{}' in uwp

I am making HTTP Post call to a webservice url. In the response I get 200 Status Ok message. But when I try to get the response content using await response.Content.ReadAsStringAsync() its always '{}'. The web service returns either SUCCESS or FAILED based on the user credentials passed. How do I retrieve the message. Please help.
Code to make a web service call
public Task<HttpResponseMessage> PostAsJsonAsync<T>(Uri uri, T item)
{
var client = new HttpClient();
var itemAsJson = JsonConvert.SerializeObject(item);
var content = new StringContent(itemAsJson);
//var content = new FormUrlEncodedContent(itemAsJson);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
client.DefaultRequestHeaders.Accept.Clear();
return client.PostAsync(uri, content);
}
private async void mainPage_Loaded(object sender, RoutedEventArgs e)
{
UserDetails details = new UserDetails()
{
username = composite["username"].ToString(),
userpass = composite["password"].ToString()
};
var response = await PostAsJsonAsync(new Uri("http://ec2-xxxx-.compute-1.amazonaws.com:8080/Sanjeevani/rest/SV/login"), details);
if (response.IsSuccessStatusCode) //I get a 200 code i.e OK
{
string str = await response.Content.ReadAsStringAsync();
if (str == "SUCCESS") //str is always '{}'
{
this.Frame.Navigate(typeof(Dashboard), details);
}
}

C#: Error when HttpClient reponse

I am doing HTTP get request using HttpClient in C# console app . I am not getting expected response with one get request.
Get Request is like
http://example.com/xyz/SearchProduct?productNo=11210&1d6rstc9xc=5jyi27htzk
I am getting some vague response but when i do same get request with fiddler it is giving expected response.
How can I get expected response from httpClient.GetAsync(url)?
code is :-
var httpClient = new HttpClient();
var url = "http://example.com/xyz/SearchProduct?productNo=11210&1d6rstc9xc=5jyi27htzk";
HttpResponseMessage response1 = await httpClient.GetAsync(url);
if (response1.IsSuccessStatusCode)
{
HttpContent stream = response1.Content;
Task<string> data = stream.ReadAsStringAsync();
}
You should read as string that way:
string result = await stream.ReadAsStringAsync();
instead of that:
Task<string> data = stream.ReadAsStringAsync();
Here full code example and another example
This is a full method using async/await approach.
private static async Task<string> GetRequestContentAsString(string url)
{
var data = string.Empty;
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var stream = response.Content;
data = await stream.ReadAsStringAsync();
}
}
return data;
}
This method is called this way:
var content = await GetRequestContentAsString("http://www.bing.com");

Categories

Resources