I have a Xamarin multiplatform application and I need people to log into my application. To check the credentials I have a API. But the code I created to call my API gives me the following error :
System.InvalidOperationException: Cannot send the same request message
multiple times
private HttpResponseMessage GetResponse(HttpMethod method, string requestUri, object value)
{
HttpRequestMessage message = new HttpRequestMessage(method, requestUri);
if (Login != null)
{
message.Headers.Add("Authorization", "Basic " + Login.AuthenticatieString);
}
if (value != null)
{
string jsonString = JsonConvert.SerializeObject(value);
HttpContent content = new StringContent(jsonString);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
message.Content = content;
}
#if DEBUG
ServicePointManager.ServerCertificateValidationCallback =
delegate (object s, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{ return true; };
#endif
HttpResponseMessage response = Client.SendAsync(message).Result;
var resp = response.Content.ReadAsStringAsync();
return response;
}
That code is being called on a button click by the following code :
Models.LoginModel login = new Models.LoginModel();
login.Login = username.Text;
login.Wachtwoord = password.Text;
var result = new ApiRequest()
.Post("api/login", login);
And if I put a breakpoint on the line where the error occurs
HttpResponseMessage response = Client.SendAsync(message).Result;
I can see that it is only executed once.
Update 1 :
The .Post function simply calls the GetResponse method like below :
public HttpResponseMessage Put(string requestUri, object value)
{
return GetResponse(HttpMethod.Put, requestUri, value);
}
As mentioned in my comment, you probably need to remove the line var resp = response.Content.ReadAsStringAsync(); from the GetResponse(...) method.
The ReadAsStringAsync() method will dispose the instance of the HttpResponseMessage class making it no longer valid. Since you do not return the resp variable but the (at this point already disposed) response variable, I assume later on somewhere in the code you make another call to this object. Which will result in the InvalidOperationException being thrown.
So try using the following function:
private HttpResponseMessage GetResponse(HttpMethod method, string requestUri, object value)
{
HttpRequestMessage message = new HttpRequestMessage(method, requestUri);
if (Login != null)
{
message.Headers.Add("Authorization", "Basic " + Login.AuthenticatieString);
}
if (value != null)
{
string jsonString = JsonConvert.SerializeObject(value);
HttpContent content = new StringContent(jsonString);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
message.Content = content;
}
#if DEBUG
ServicePointManager.ServerCertificateValidationCallback =
delegate (object s, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{ return true; };
#endif
HttpResponseMessage response = Client.SendAsync(message).Result;
return response;
}
Related
I'm trying to make this return a templated object which is a different type that what is sent. How do I do it?
UserSSS user = new UserSSS();
ReturnObject foo = await _callServer.PostAsync($"{Constants.SERVER_URL}/profiles/v1/HelloObject", user);
public async Task<T> PostAsync<T>(string url, T toPost)
{
HttpClientHandler httpClientHandler = new HttpClientHandler();
#if DEBUG
httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
#endif
string json = JsonConvert.SerializeObject(toPost);
StringContent httpContent = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
using (var client = new HttpClient(httpClientHandler))
{
var ret = await client.PostAsync(url, httpContent);
string contents = await ret.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject(contents);
}
}
} ```
Do you mean different generic input and output types? If yes then it's simple as:
public async Task<TOut> PostAsync<TIn, TOut>(string url, TIn toPost)
and in the return
return JsonConvert.DeserializeObject<TOut>(contents);
I would like to return a 422 Response from my API which includes as much of the data model that I can. I've created a method in our BaseRepository where we pass in the data, and an optional message, to return as a 422.
protected IHttpActionResult Create422Response<TData>(
TData data,
string message = "Resource validation failed."
)
{
var response = new ResponseObject<TData>(data)
{
Status = "Failure",
Message = message
};
return Content((HttpStatusCode)422, response);
}
On the client side, I'm catching the response like so:
var response = await _orderApiClient.ShowOrderForApproval(id);
if (response.StatusCode == (HttpStatusCode)422)
{
Logger.LogWarning(response.Content);
var responseJson = JObject.Parse(response.Content);
ProcessValidationErrorsFromResponse(response);
}
When I look at the value of response.Content, I see that the JSON is truncated.
If I pass the same data object through an OK response it works.
protected IHttpActionResult CreateOkResponse<TData>(TData data, string message = "Call was successful")
{
var response = new ResponseObject<TData>(data)
{
Status = "Success",
Message = message
};
return Ok(response);
}
Why would the 422 truncate the data? Could there be something else going on?
UPDATE:
Here's what ShowOrderForApproval does:
public async Task<IRestResponse> ShowOrderForApproval(int id)
{
var request = new RestRequest("/api/orders/{id}/approve/")
{
Method = Method.GET,
RequestFormat = DataFormat.Json
};
request.AddUrlSegment("id", id.ToString());
return await RsClient.SendRequestAsync(request, new HttpContextWrapper(HttpContext.Current));
}
RsClient.SendRequestAsync is:
public static Task<IRestResponse> SendRequestAsync(RestRequest request, HttpContextBase context)
{
if (_client == null)
{
_client = Connect();
}
//If null, this is coming from the CI API and we've stored the value in the originating request header
if (context.Session == null)
{
var authHeaderValue = context.Request.Headers["Authorization"];
request.AddHeader("Authorization", "Bearer " + authHeaderValue);
}
else
{
var sessionTokenName = ConfigurationManager.AppSettings["SessionTokenName"];
request.AddHeader("Authorization", "Bearer " + context.Session[sessionTokenName]);
}
return Task.FromResult(_client.Execute(request));
}
Update 2:
Okay, more updates. Using Postman I can get all the data with a 422. Our RsClient is using RestSharp.
I'm trying to manage the redirect on this site: https://uk.soccerway.com, so I wrote this code:
public static string GetHtml(string url)
{
HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(url);
try
{
webReq.AllowAutoRedirect = false;
HttpWebResponse response = webReq.GetResponse() as HttpWebResponse;
WebClient client = new WebClient();
client.Encoding = System.Text.Encoding.UTF8;
url = (response.Headers["Location"] != null) ? response.Headers["Location"] : url;
return client.DownloadString(url);
}
catch (WebException)
{
throw;
}
}
this will return an Exception, in particular:
'The remote server returned an error: (302) Moved Temporarily.'
what I did wrong?
I can see this being a useful answer for many other users so reading this article helped me arrive with the code below. It recursively handles the redirects in case there are more than 1 redirects. Finally it returns the response:
public static HttpResponseMessage MakeRequest(string url)
{
using (var client = new HttpClient())
{
var request = new HttpRequestMessage()
{
RequestUri = new Uri(url),
Method = HttpMethod.Get
};
HttpResponseMessage response = client.SendAsync(request).Result;
var statusCode = (int)response.StatusCode;
// We want to handle redirects ourselves so that we can
// determine the final redirect Location (via header)
if (statusCode >= 300 && statusCode <= 399)
{
var redirectUri = response.Headers.Location;
if (!redirectUri.IsAbsoluteUri)
{
redirectUri = new Uri(request.RequestUri.
GetLeftPart(UriPartial.Authority) + redirectUri);
}
return MakeRequest(redirectUri.AbsoluteUri);
}
return response;
}
}
Usage
HttpResponseMessage response = MakeRequest("https://uk.soccerway.com");
if (response.IsSuccessStatusCode)
{
string content = response.Content.ReadAsStringAsync().Result;
}
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);
}
}
I want to send a http post that can take a couple of seconds to reply without freezing my UI, currently this code just hangs my application when the method is callled.
What am I doing wrong and how do i achieve my goal?
private async Task<string> DoHttpClientPost(string method, IDictionary<string, object> args = null)
{
{
HttpClientHandler handler = new HttpClientHandler()
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
handler.Proxy = null;
HttpResponseMessage response;
using (var myHttpClient = new HttpClient(handler))
{
myHttpClient.DefaultRequestHeaders.ExpectContinue = false;
myHttpClient.DefaultRequestHeaders.Add("Accept-Charset", "ISO-8859-1,utf-8");
myHttpClient.DefaultRequestHeaders.Add(APPKEY_HEADER, CustomHeaders.GetValues(APPKEY_HEADER));
myHttpClient.DefaultRequestHeaders.Add(SESSION_TOKEN_HEADER, CustomHeaders.GetValues(SESSION_TOKEN_HEADER));
myHttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json-rpc"));
var call = new JsonRequest { Method = method, Id = 1, Params = args };
var jsonObject = JsonConvert.Serialize<JsonRequest>(call);
var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json-rpc");
response = await myHttpClient.PostAsync(new Uri(EndPoint), content);
}
Console.WriteLine("\nCalling: " + method + " With args: " + JsonConvert.Serialize<IDictionary<string, object>>(args));
string jsonResponse = await response.Content.ReadAsStringAsync();
return jsonResponse;
}
}
public T Invoke<T>(string method, IDictionary<string, object> args = null)
{
if (method == null)
throw new ArgumentNullException("method");
if (method.Length == 0)
throw new ArgumentException(null, "method");
var jsonString = DoHttpClientPost(method, args).Result;
var jsonResult = JsonConvert.Deserialize<JsonResponse<T>>(jsonString);
return jsonResult.Result;
}
var jsonString = DoHttpClientPost(method, args).Result;
This is your culprit. If you call .Result on a Task from the UI thread it will hang.
You'll need to async all the way up - so Invoke should be async and return a Task<T> and await the DoHttpClientPost call, the caller should be async etc. etc. etc.
You have to make two changes
Modify this line from
response = await myHttpClient.PostAsync(new Uri(EndPoint), content);
to
response = await myHttpClient.PostAsync(new Uri(EndPoint), content).ConfigureAwait(false);
And looks like your intention is to wait for the post call to complete and return the results, so modify this line from
var jsonString = DoHttpClientPost(method, args).Result;
to
var jsonStringTask = DoHttpClientPost(method, args);
jsonStringTask.Wait(); //wait for http post call to complete.
var jsonString = jsonStringTask.Result;