Gracefully handling an empty json object in RestSharp - c#

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;
}
}

Related

How to convert List<object> to List<CustomClass>?

I'm building a class library that connects to a client's API, and collects the response from a given endpoint into a class. In other words, I'll hit the client's Donuts endpoint, and using JsonConvert to deserialize the response into my Donuts class, which has fields that match the attributes in the JSON object. This is all well and good, but they have a lot of endpoints, and I have many methods which repeat the same code over and over. In the interests of DRYness and separation of concerns, I'd like to have a separate namespace that makes the rest call, no matter the endpoint, and returns the correct result to the main class. Here's an example of what it does now (I'm using RestSharp to configure the request and response):
public List<T> Index<T>(string Endpoint)
{
List<T> Results = new List<T>();
RestClient Client = new RestClient();
RestRequest Req = ConfigureGetRequest(Endpoint);
IRestResponse Resp = Client.Execute(Req);
if (Resp.StatusCode == System.Net.HttpStatusCode.OK)
{
Results = JsonConvert.DeserializeObject<List<T>>(Resp.Content, DeserializationSettings);
}
return Results;
}
}
There's other code in the if statement (it's actually an if-else for error handling), but it's not really relevant to this question. I have this same code in multiple methods, with the only difference being that it returns List<Bagel>, or List<Cruller>, etc... What I'd like to do is extract this code into it's own method, but I'm not sure how. Right now, I'm trying to deserialize into List<object>, and then convert it to List<Donut> later, but that doesn't seem to be something I can do. My other thought is to pass in the endpoint name as a parameter (public List<Donut> GetDonuts(string Endpoint)), and then somehow use that to determine what type of object to deserialize into, if that makes sense? Anyhoo, if anyone has any insight on a good way to do this, it would be appreciated!
EDIT
Awesome responses, thank you folks. I've refactored the method according to Matt Dillard's answer below. The next step is to convert the List to List. The method that calls the method above looks like this:
public List<Donut> GetDonuts()
{
List<Donut> Results = new List<Donut>();
MODULE MakeCall = new GET();
return Results = MakeCall.Index("donuts")();
}
My Intellisense tells me that "The type arguments for method 'MODULE.Index(string)' cannot be inferred from the usage. Try specifying the type arguments explicitly". I tried casting like so: (List)MakeCall.Index("donuts")();
but that didn't work out. Is there a way to cast this so that I can have methods GetDonuts, GetCrullers, etc... that all call Index effectively?
Generics:
public List<T> GetStuff<T>(string endpoint)
{
List<T> Results = new List<T>();
RestClient Client = new RestClient();
RestRequest Req = ConfigureGetRequest(endpoint);
IRestResponse Resp = Client.Execute(Req);
if (Resp.StatusCode == System.Net.HttpStatusCode.OK)
{
Results = JsonConvert.DeserializeObject<List<T>>(Resp.Content, DeserializationSettings);
}
return Results;
}
}
Bascially replace all references to Donut with T.
Presumably the endpoint address would change, so you can add that as a parameter.
Call something like this:
var donuts = GetStuff<Donut>("http://dunkin.com/api/allthedonuts");
You probably would like to have it as generic method which accepts a type T and return a list of such like
public List<T> GetDonuts<T>() where T : class, new()
{
I figured out what needed to happen. I can't deserialize a generic list, but I can deserialize a generic, and place it in a list. The final code looks like this:
public List<Donut> GetDonuts<T>()
{
GET MakeCall = new GET();
List<Donut> Results = new List<Donut>();
Results = MakeCall.Index<Donut>();
return Results;
}
public List<T> Index<T>()
{
T Result;
List<T> ResultList = new List<T>();
RestClient Client = new RestClient();
RestRequest Req = ConfigureGetRequest(Endpoint);
IRestResponse Resp = Client.Execute(Req);
if (Resp.StatusCode == System.Net.HttpStatusCode.OK)
{
Result = JsonConvert.DeserializeObject<T>(Resp.Content, DeserializationSettings);
ResultList.Add(Result);
}
return ResultList;
}
This allows me to use GetDonuts, GetCrullers, etc... to be lean and DRY. Thanks for all the help!

ASP.NET MVC handle request error

In my .Net MVC app I need to handle server side validation. If there was something wrong with request I get this:
{
"validationMessage": message
}
with StatusCode = 200.
Otherwise of course I get response proper for the call. My issue is that I have troubles checking for validation messages and then deserializing response (I always get null there though fiddler shows me that response comes back).
public static async Task<Response<T>> Response<T>(HttpResponseMessage response)
{
var res = new Response<T>();
if (response.IsSuccessStatusCode)
{
//check for validation messages
var serverErrorInfo = await response.Content.ReadAsAsync<ServerError>();
if (serverErrorInfo.ValidationMessage != null)
{
res.ErrorInfo = new ErrorInfo(serverErrorInfo.ValidationMessage);
}
else
{
var result = await response.Content.ReadAsAsync<T>();
res.IsSuccess = true;
res.Result = result;
}
return res;
}
What am I doing wrong? Does the response get disposed after first attempt to read it as a ServerError? Since I use generics I cannot first check if there is response and than read the validationMessage.
ServerError Code:
[JsonObject]
public class ServerError
{
public string validationMessage{ get; set; }
}
Probably a deserialization issue. Have you tried ValidationMessage with a capital V in the json response? Also, is serverErrorInfo null entirely, or is just the property ValidationMessage null? Can you check what the value of response.Content is, right before deserialization to ServerError?
In the end the solution was just using some ugly code:
var serverErrorInfo = JsonConvert.DeserializeObject<ServerError>(await response.Content.ReadAsStringAsync());
I am very unsure why ReadAsAsync fails there.

Getting response body on failed request with HttpRequestException

I am trying to log failed requests from my HttpRequestException.
My server returns error code and additional JSON payload at the response body. I need to access that JSON. How do I read the response body in case of an errored request? I know that the actual response is not null. It's an API and I confirm that it returns JSON payload with 4xx status codes, giving detailed insight about the error.
How do I access it? Here is my code:
using (var httpClient = new HttpClient())
{
try
{
string resultString = await httpClient.GetStringAsync(endpoint);
var result = JsonConvert.DeserializeObject<...>(resultString);
return result;
}
catch (HttpRequestException ex)
{
throw ex;
}
}
I am trying to get data in throw ex line, but I couldn't find a way.
Exactly as #Frédéric suggested, if you use GetAsync method you'll get the proper HttpResponseMessage object which give more information about the response. To get details when error occurs you can desearlize the errors to an Exception or your custom exception object from Response content like below:
public static Exception CreateExceptionFromResponseErrors(HttpResponseMessage response)
{
var httpErrorObject = response.Content.ReadAsStringAsync().Result;
// Create an anonymous object to use as the template for deserialization:
var anonymousErrorObject =
new { message = "", ModelState = new Dictionary<string, string[]>() };
// Deserialize:
var deserializedErrorObject =
JsonConvert.DeserializeAnonymousType(httpErrorObject, anonymousErrorObject);
// Now wrap into an exception which best fullfills the needs of your application:
var ex = new Exception();
// Sometimes, there may be Model Errors:
if (deserializedErrorObject.ModelState != null)
{
var errors =
deserializedErrorObject.ModelState
.Select(kvp => string.Join(". ", kvp.Value));
for (int i = 0; i < errors.Count(); i++)
{
// Wrap the errors up into the base Exception.Data Dictionary:
ex.Data.Add(i, errors.ElementAt(i));
}
}
// Othertimes, there may not be Model Errors:
else
{
var error =
JsonConvert.DeserializeObject<Dictionary<string, string>>(httpErrorObject);
foreach (var kvp in error)
{
// Wrap the errors up into the base Exception.Data Dictionary:
ex.Data.Add(kvp.Key, kvp.Value);
}
}
return ex;
}
Usage:
using (var client = new HttpClient())
{
var response =
await client.GetAsync("http://localhost:51137/api/Account/Register");
if (!response.IsSuccessStatusCode)
{
// Unwrap the response and throw as an Api Exception:
var ex = CreateExceptionFromResponseErrors(response);
throw ex;
}
}
Here's the source article detailing more about handling the HttpResponseMessage and it's content.
Use GetAsync instead of GetStringAsync. GetAsync will not throw an exception and will allow you to access response content, status code and any other header you may require.
See this page for more.
Essentially what #RyanGunn posted but implemented in your code.
You should be able to ReadAsStringAsync from the resultString.Content
I am working on an SDK that employs similar code except we use a switch statement to check for various HttpStatusCodes that we intend to return before the DeserializeObject line.
using (var httpClient = new HttpClient())
{
try
{
string resultString = await httpClient.GetStringAsync(endpoint);
var result = JsonConvert.DeserializeObject<...>(resultString.Content.ReadAsStringAsync().Result);
return result;
}
catch (HttpRequestException ex)
{
throw ex;
}
}

RestSharp weird error on Method.Put

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

Build a generic helper for RestSharp for CRUD operations

I have started using RestSharp to call an webapi proejct as it seems pretty easy to use.
I am wanting to build a helper class for all of my crud actions.
I have this so far for a simple PUT request.
public static IRestResponse Update(object objectToUpdate,string apiEndPoint)
{
var client = new RestClient(CreateBaseUrl(null))
{
Authenticator = new HttpBasicAuthenticator("user", "Password1")
};
var request = new RestRequest(apiEndPoint, Method.PUT);
request.AddObject(objectToUpdate);
var response = client.Execute<MyViewModel>(request);
//var response = client.ExecuteDynamic(request);
return response;
}
So the above code works however I have had to hardcode my viewmodel into it
var response = client.Execute<MyViewModel>(request);
How can I change this so I dont need to know the type of model I am expecting?
I tried using var response = client.ExecuteDynamic(request);
however this throws an exception of
Unable to cast object of type 'RestSharp.RestResponse' to type 'RestSharp.RestResponse`1[System.Object
Im not sure how I am meant to cast my object correctly
I'm not familiar with RestSharp. However, it sounds like generics could help you here. Either your class or method needs to accept a type. For example, the signature of your method would change to
public static IRestResponse Update<T>(object objectToUpdate,string apiEndPoint)
This would allow you to call the method as:
Update<MyViewModel>(objectToUpdate, apiEndPoint);
Your implementation would change from your concrete type to:
var response = client.Execute<T>(request);
Overall you could modify your code to something like this:
public static IRestResponse Update<T>(object objectToUpdate,string apiEndPoint)
{
var client = new RestClient(CreateBaseUrl(null))
{
Authenticator = new HttpBasicAuthenticator("user", "Password1")
};
var request = new RestRequest(apiEndPoint, Method.PUT);
request.AddObject(objectToUpdate);
var response = client.Execute<T>(request);
//var response = client.ExecuteDynamic(request);
return response;
}
Documentation on C# Generics can be found here: http://msdn.microsoft.com/en-us/library/ms379564(v=vs.80).aspx

Categories

Resources