I'm trying to update a resource using RestSharp. The API works well because are used in another application so this exclude some routing issues or whatever the problem is on my side not on API one.
Anyways. My current scenario is that I want to update a specific resource located at host/api/resource/id
This is my current code inside the DataProvider layer
public override bool Update(string resource, Dictionary<string, object> properties)
{
this.request = new RestRequest(resource + "/{id}", Method.PUT);
for (int i = 0; i < properties.Count; ++i)
{
KeyValuePair<string, object> kvp = properties.ElementAt(i);
if (kvp.Key != "id")
this.request.AddParameter(kvp.Key, kvp.Value, ParameterType.GetOrPost);
else
this.request.AddParameter(kvp.Key, kvp.Value, ParameterType.UrlSegment);
}
var response = this.CallApi();
// ... other stuff
}
This code simply create the request and the correct parameters based on the dictionary that the method received from outside, then it calls the CallApi() method which is this
private IRestResponse CallApi()
{
var client = new RestClient(BaseUrl);
var response = client.Execute(this.request);
if(response.ErrorException != null)
{
// Response has some error!
// ... other stuff
}
if(response.StatusCode != System.Net.HttpStatusCode.OK)
{
// Response received something different from HTTP status code OK
// ... other stuff
}
return response;
}
CallApi works perfectly for every other call such as GET, POST, DELETE and even PATCH but when I try to use it with Update, and thus using PUT, the response received from client.Execute(this.request) is 405 Method Not Allowed.
After debugging a little bit I figured it out that the respone has a ResponseUri with only the host string instead of host/api/resource/id this seems to be caused by the
this.request = new RestRequest(resource + "/{id}", Method.PUT);
in fact if I remove the /{id} part, the RequestUri has the correct form of host/api/resource, of course without the id, which is wrong anyway because I need the id :-/
Does anyone know why this is happening?
The problem was at the backslash on new Instance. Just remove the backslash from /{id} and it works
Related
I am issuing a POST request through the HttpClient class and reading the response. It works about 90% of the time, but sometimes the response string is {} and the endpoint I am hitting behaves as though it was issued a DELETE request with the same parameters as my POST request. This is my code
while (true)
{
HttpContent content = new FormUrlEncodedContent(new Dictionary<string, string>());
string fliptUrl = _fliptUrl + flag;
var response = _http.PostAsync(fliptUrl, content).Result;
if (!response.IsSuccessStatusCode)
{
continue;
}
string responseString = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(responseString);
if (responseString.Equals("404 page not found\n"))
{
throw new HttpRequestException("Flipt returned 404");
} else if (responseString.Equals("{}"))
{
continue;
}
Why would this be happening? Am I handling the asynchronous nature of the HttpClient in a weird way that is resulting in this behavior?
The print statement shown usually prints out {"key":"asdf","name":"asdf","description":"asdf","enabled":true,"createdAt":"2019-08-09T18:59:37.926184281Z","updatedAt":"2019-08-09T18:59:37.926184281Z"} and in the edge case I am encountering it prints {}
Hello all am trying to do a login to my xamarin api using RestSharp, the API ought to return status code 200 OK if the authentication works and status code 415 if the authentication fails(wrong password) and other codes depending on what the case scenario, but instead i get a status code 0 on all other case asides when the authentication pass(status code 200 ok), the source code below is how i implement
//payload am sending to the api
RequestPayload res = new RequestPayload();
res.appid = appid;
res.data = data;
res.method = "Login";
//convert to json object
var MySerializedObject = JsonConvert.SerializeObject(res);
string APIUrl = ""http://142.168.20.15:8021/RouteTask";
//create client
RestClient client = new RestClient(APIUrl);
//create request
RestRequest request = new RestRequest(Method.POST);
// set request headeer
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
//request.AddJsonBody(MySerializedObject); --i have also tried this
request.AddParameter("application/json", MySerializedObject, ParameterType.RequestBody);
request.JsonSerializer.ContentType = "application/json; charset=utf-8";
request.AddParameter("RequestSource", "Web", "application/json", ParameterType.QueryString);
client.Timeout = 2000000;
var response = client.Execute(request); // where the issue appears
//RestResponse response = client.Execute(request); // i have tried this
//IRestResponse response = client.Execute(request); // i have tried this
if (response.IsSuccessful)
{
//use response data
}
on all scenerio it comes back with a StatusCode: 0, Content-Type: , Content-Length: 0) and errorMessage
"Error getting response stream (ReadAsync): ReceiveFailure Value
cannot be null. Parameter name: src"
screenshot below indicate when the api call fails
Response receieved when the authentication is valid
I was finally able to find a workaround for this. Bear with the long-winded response.
The tags mention Xamarin, which is what I am working in as well - specifically with iOS. I think it may actually be a bug with Mono, but I didn't take it that far to confirm.
The problem lies with the default way of copying the response buffer. In the RestSharp code, this is done by an extension method in MiscExtensions.cs called ReadAsBytes. It appears that with certain response buffers, the call to the Stream.Read method is failing. When this happens, the exception causes RestSharp to "shortcut" the rest of the processing on the response, hence the status code never gets filled in since it happens after the call to ReadAsBytes.
The good news is RestSharp does give a way to replace this call to ReadAsBytes with one of your own. This is done via the ResponseWriter property on the IRestRequest object. If it has a function defined, it will bypass the ReadAsBytes call and call the function you gave it instead. The problem is, this is defined as an Action and you don't get a copy of the full response object, so it's somewhat useless. Instead you have to use the AdvancedResponseWriter property. This one includes both the response object and the response stream. But you still have to set the ResponseWriter property or it won't bypass the default handler and you'll still get the error.
Ok, so how do you make this work? I ended up implementing it as a wrapper to RestClient so I wouldn't have to implement the code all over the place. Here's the basic setup:
public class MyRestClient : RestClient
{
public MyRestClient(string baseUrl) : base(baseUrl)
{ }
public override IRestResponse Execute(IRestRequest request)
{
request.ResponseWriter = s => { };
request.AdvancedResponseWriter = (input, response) => response.RawBytes = ReadAsBytes(input);
return base.Execute(request);
}
private static byte[] ReadAsBytes(Stream input)
{
var buffer = new byte[16 * 1024];
using (var ms = new MemoryStream())
{
int read;
try
{
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{ ms.Write(buffer, 0, read); }
return ms.ToArray();
}
catch (WebException ex)
{ return Encoding.UTF8.GetBytes(ex.Message); }
};
}
}
The ReadAsBytes method is actually just a copy/paste of the RestSharp ReadAsBytes method with the addition of a try/catch. If it fails, it returns the exception reason in to the response buffer. This may or may not be what you want, so modify as needed. You may also need to override other methods for Execute, but in my case this is the only one we're using so it was enough.
So far this seems to be doing the trick for me. Perhaps if someone got ambitious they could trace it all the way in to Mono to try and see what it doesn't like about the stream, but I don't have the time for it at the moment.
Good luck!
OK so after toying around with RestSharp for a bit, i realize just as #steve_In_Co mentioned earlier there were compatibility issues with MONO (we presume this is a bug) so i did it in a basic way using the .Net HTTP library and it works for me, so in case someone is still looking for a way out, find the working .net http implementation code below.
//payload am sending to the api
RequestPayload res = new RequestPayload();
res.appid = appid;
res.data = data;
res.method = "Login";
//convert to json object
var MySerializedObject = JsonConvert.SerializeObject(res);
string APIUrl = ""http://142.168.20.15:8021/RouteTask";
//create basic .net http client
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(APIUrl);
// this was required in the header of my request,
// you may not need this, or you may need to adjust parameter
//("RequestSource","Web") or you own custom headers
client.DefaultRequestHeaders.Add("RequestSource", "Web");
// this class is custom, you can leave it out
connectionService = new ConnectionService();
//check for internet connection on users device before making the call
if (connectionService.IsConnected)
{
//make the call to the api
HttpResponseMessage response = await
client.PostAsJsonAsync(ApiConstants.APIDefault, res);
if (response.IsSuccessStatusCode)
{
string o = response.Content.ReadAsStringAsync().Result;
dynamic payload = JsonConvert.DeserializeObject(o);
string msg = payload["valMessage"];
resp.a = true;
resp.msg = payload["responseDescription"];
}
else
{
string o = response.Content.ReadAsStringAsync().Result;
dynamic payload = JsonConvert.DeserializeObject(o);
resp.a = false;
resp.msg = payload["response"];
}
}
I'm creating an application with Xamarin.Forms which consume from SOAP services using POST method; I have a bunch of services that work correctly, indeed, one of these methods is used to send information in multiple cases and my problem is related with this.
I have the following HttpClient method, but sometimes doesn't work... unfortunately I don't have access to back-end code and they are not for the labour of help me with that.
Any idea about how to improve my method or get any approach to the real error? I'm stuck here, since I send the same fields each time.
public async Task<string> InvokeAsync (string uri, string xmlSOAP) {
try {
using (var handler = new HttpClientHandler () { UseCookies = false })
using (var client = new HttpClient (new NativeMessageHandler ())) {
client.DefaultRequestHeaders.Accept.Add (new MediaTypeWithQualityHeaderValue ("application/xml"));
client.DefaultRequestHeaders.Add ("Cache-Control", "no-cache, no-store, must-revalidate");
client.DefaultRequestHeaders.Add ("Pragma", "no-cache");
client.Timeout = TimeSpan.FromSeconds (timeout);
client.DefaultRequestHeaders.CacheControl.NoCache = true;
var req = new HttpRequestMessage (HttpMethod.Post, uri)
{
Content = new StringContent (xmlSOAP, Encoding.UTF8)
};
req.Content.Headers.ContentType = MediaTypeHeaderValue.Parse ("text/xml; charset=utf-8");
if (uri.ToLowerInvariant ().Equals (jsessionUrlCheck)) {
if (jsession != null && jsession.Count > 0) {
foreach (var cookie in jsession) {
req.Headers.Add ("JSESSIONID", cookie);
}
}
jsession = null;
}
HttpResponseMessage response = await client.SendAsync (req);
string responseBodyAsText = response.IsSuccessStatusCode ? await response.Content.ReadAsStringAsync () : string.Empty;
if (!string.IsNullOrEmpty (responseBodyAsText))
{
return responseBodyAsText;
}
return null;
}
} catch (Exception e) {
Debug.WriteLine ("========= InvokeAsync Exception =========");
Debug.WriteLine ("Error: " + e.Message);
return null;
}}
Any idea about how to [...] get any approach to the real error?
It sounds like you don't really know what exactly happens when it "doesn't work". The way you approach the real error is by finding out what exactly happens in this code when it is reported not to work.
Do you have logs? Check the logs. If the exception is there, that should point you in the right direction. Exception not there? Maybe start logging the data received too. No logs? Start logging; there's no better way to handle intermittent failures that you can't reproduce on demand.
I'm posting some data to an web api controller method from an MVC controller with this method..
private static async Task<HttpResponseMessage> SendDataToApi (List<TogglRow> input)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:****/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.PostAsJsonAsync("api/service", input);
//if (response.StatusCode == HttpStatusCode.OK)
//{
// var resultUri = response.Headers.Location;
//}
return response;
}
}
This is the Web Api method i'm posting to..
public HttpResponseMessage Post(HttpRequestMessage request, List<Dagsrapport> value)
{
if (value != null)
{
var rapporter = value.ToList();
//send rapporter to DB
var response = new HttpResponseMessage(HttpStatusCode.OK);
return response;
}
return request.CreateResponse(HttpStatusCode.BadRequest);
}
Now, the post works fine and i'm returning HttpStatusCode.OK. But i'm not beeing redirected back to the method i'm performing the post from (SendDataToApi()). I'm beeing returned back to the page from wich the post was triggered. I can see the page is working (waiting for localhost..) but nothing happens.
I should probably mention that this is two separate projects (MVC & WebApi), but in the same solution.
What am i missing?
EDIT - Solved
The problem I had was due to the method that ran the task "SendDataToApi" was not set to async. Therefore, it did not wait for an results from the post, but instead ran synchronously and the control never returned to the method that ran SendDataToApi, instead it returned to the original caller - the UI.
Here is the method that is runnig the SendDataToApi task..
[HttpPost]
public async Task<ActionResult> Edit(IEnumerable<TogglRow> tr)
{
var listToExport = tr.Where(x => x.Export.Equals(true));
var result = listToExport.ToList();
var response = await SendDataToApi(result);
return RedirectToAction("Index", "Home",
response.StatusCode == HttpStatusCode.OK ? new { message = "Record(s) were successfully stored." } : new { message = "No records selected." });
}
It seems you have some fundamental misunderstandings about how all this works. MVC actions and WebAPI actions work very differently, which is why they're actually in entirely different namespaces, even though they both implement similar components.
If you need to connect to a Web API from an MVC action, you shouldn't be receiving the response as an HttpResponseMessage. That's a return value for a WebAPI action, similar to how a ViewResult is a return value for an MVC action. It has no meaning to anything in MVC. Rather, your actual response from HttpClient, for example, will be a string (technically a byte array) with a content type indicating that it should be interpreted as plain text, JSON, XML, etc.
Based on the content type, you'll process this response accordingly. If it's JSON, for example, then you can use something like JObject from Newtonsoft.Json (default JSON interpreter in MVC). Then, you could use this data object to construct your response for your MVC action. If you have something indicating that a redirect should be made, then the MVC action can return on of the Redirect* family of results. Importantly, you can't just make the redirect the response of the Web API action, because that merely affects the HttpClient response object.
I have the following code:
public void GetJson()
{
RestRequest request = new RestRequest(Method.GET);
var data = Execute<Dictionary<string, MyObject>>(request);
}
public T Execute<T>(RestRequest request) where T : new()
{
RestClient client = new RestClient(baseUrl);
client.AddHandler("text/plain", new JsonDeserializer());
var response = client.Execute<T>(request);
return response.Data;
}
The problem is that sometimes the response will be an empty json array []. And when I run this code I get the following exception: Unable to cast object of type 'RestSharp.JsonArray' to type 'System.Collections.Generic.IDictionary`2[System.String,System.Object]'.
Is there a way to gracefully handle this?
I worked around a similar issue myself in the following way. I had tried using custom deserializers (since I was deserializing to a complex object) but in the end the following was much simpler, as it only applied to one of the many kinds of requests I was making.
request.OnBeforeDeserialization = (x =>
{
x.Content = x.Content.Replace("[]", "{}");
});
Where I was constructing the request object for this particular request, I used the OnBeforeDeserialization property to set a callback which replaces the incorrect [] with {}. This works for me because I know the data I'm getting back in the rest of x.Content will never contain [] except in this specialized case, even in the values.
This might help someone else, but should definitely be used with care.
I've never needed the client.AddHandler line, so I'm not sure you need that. Try this for your Execute method, though:
public T Execute<T>(RestRequest request) where T : class, new()
{
RestClient client = new RestClient(baseUrl);
client.AddHandler("text/plain", new JsonDeserializer());
try
{
var response = client.Execute<T>(request);
return response.Data;
}
catch (Exception ex)
{
// This is ugly, but if you don't want to bury any errors except when trying to deserialize an empty array to a dictionary...
if (ex is InvalidCastException && typeof(T) == typeof(Dictionary<string, MyObject>))
{
// Log the exception?
return new T();
}
throw;
}
}