-
-
Notifications
You must be signed in to change notification settings - Fork 132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Error Handling #275
Error Handling #275
Conversation
… tweaks for handling nulls in case of not founds
|
||
namespace TMDbLib.Objects.Exceptions | ||
{ | ||
public class NotFoundException : TMDbHttpException |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably not name our exceptions "Http". Sure. If something is http specific, go for it - but for the higher level stuff, like "not found", the inheritance tree should not include "Http" (or any other transport protocol).
{ | ||
public class NotFoundException : TMDbHttpException | ||
{ | ||
public NotFoundException(HttpStatusCode httpStatusCode, TMDbStatusMessage statusMessage) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In principle, this exceptionshould not know anything about any HttpStatusCode.
TMDbStatusMessage can stay - but we may encounter a scenario in which we don't have data for it. I'll try listing some exception types on the issue.
|
||
[JsonProperty("status_message")] | ||
public string StatusMessage { get; set; } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the object from TMDb?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, an example could be: http://api.themoviedb.org/3/credit/9999999999?api_key=c6b31d1cdad6a56a23f0c913e2482a31
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good.
TMDbLib/Rest/RestRequest.cs
Outdated
{ | ||
if (response.StatusCode == HttpStatusCode.Unauthorized) | ||
throw new UnauthorizedAccessException("Call to TMDb returned unauthorized. Most likely the provided API key is invalid."); | ||
} | ||
}**/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unecessary code.
TMDbLib/Rest/RestRequest.cs
Outdated
statusMessage = | ||
JsonConvert.DeserializeObject<TMDbStatusMessage>(await resp.Content.ReadAsStringAsync()); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will there always be a Status message object in erronous responses?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aha. Just read #270 - we've seen HTML before.
Perhaps we should also check the Content-Type on the response. If it is not JSON (application/json), it's an error, and we should throw the GeneralException
-- regardless if it's a known response, such as 404.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, not necesarily. An issue was reported were the API returned HTML instead of JSON. Not sure how to handle it exactly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check for the Content-Type. I imagine that after checking for 429 responses, we check if the Content Type is json. If it isn't, then it's an error no matter what the status code is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can also move the parsing down to the other if..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Want to throw a specific exception for that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. For response other JSON - no. It's an error, yes, but it should feed back in to the rest of the loop.
We just should not even attempt to deserialize a status message (or an object) - if the content type is not json.
|
||
namespace TMDbLib.Objects.Exceptions | ||
{ | ||
public class UnhandledException : TMDbHttpException |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's name this "GeneralException".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also. As this is explicitly a protocol issue, we could name this "GeneralHttpException", and include the HttpStatusCode here.
TMDbLib/Rest/RestResponse.cs
Outdated
} | ||
else | ||
{ | ||
return default(T); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, I'd rather fail. IIRC, the RestResponse is never exposed -- so we are in complete control of calls.
For this case, it should be "if (!Valid) throw new ..".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my comment in the PR thread about this line.
TMDbLib/Rest/RestResponse.cs
Outdated
@@ -17,6 +17,8 @@ public RestResponse(HttpResponseMessage response) | |||
Response = response; | |||
} | |||
|
|||
public bool Valid { get { return Response != null; } } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably be named "IsValid"
TMDbLibTests/ClientCreditTests.cs
Outdated
[Fact] | ||
public void TestMissingCredit() | ||
{ | ||
Credit result = Config.Client.GetCreditsAsync("9999999999").Result; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should make a single test that tests the exception-throwing part. Testing each "area" of TMDb once for not-found is probably a good idea. That API hasn't really shined in concistency :)
Looks really good. NullsFor nulls, I'd prefer that if the ThrowOnNotFound option was set, we threw errors on api calls (which this PR does, iirc). When it's not set, we return nulls and generally try as much as possible to not throw errors.. Perhaps we should go through all "throw new" statements... Internally. There was one place in RestResponse where you returned Perhaps ExceptionsAs for exceptions, I imagine we can do something like this:
Another more flat approach:
|
The problem with returning nulls directly from Furthermore, wouldn't we like to distinguish between throwing errors on "not founds" and general errors? |
Throwing not founds, generals etc. happens all the way down in RestResponse. So that's already "handled". What we need to figure out is what happens when we don't throw errors. I now remember all the touchup happening afterwards. Jibbers. That code shouldn't run if there is no object to run on. Having a Encapsulating this patchning to an action would equally be bad, as it's more complex and less forgiving .. (something like Hrmm.. We could change the internal request, to a Try* pattern. Something like this.
This will force the consumers (us, internally) to remember that this can "not-fail" but still "fail". |
Arh crap. The calls are async, so out-parameters are a no-go. The only option then is to encapsulate the response in a container which indicates success.. Which, surprise(!), we already have - it now has a success indicator called "IsValid". ... So we're left with this.
Which, as you correctly point out, is cleaner by just returning null... Crap.. -.- |
TMDbLib/Client/TMDbClientMovies.cs
Outdated
@@ -101,7 +101,7 @@ public partial class TMDbClient | |||
RestResponse<Movie> response = await req.ExecuteGet<Movie>(cancellationToken).ConfigureAwait(false); | |||
|
|||
// No data to patch up so return | |||
if (response == null) return null; | |||
if (response == null || !response.IsValid) return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't check for IsValid in some places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is also related to trying to return null.. Response isn't null, as it's a wrapper containing the null value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another thing to fix then is that if ExecuteGet always returns non-null - we should never check for null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's probably a good idea.
TMDbLib/Rest/RestRequest.cs
Outdated
statusMessage = | ||
JsonConvert.DeserializeObject<TMDbStatusMessage>(await resp.Content.ReadAsStringAsync()); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can also move the parsing down to the other if..
…l exception in case of weird results
I think I might have covered most of the cases where we should return null now. Any further comments, changes etc? |
I'm looking through the code now in VS with R# - I'm doing a number of its suggested changes. I'll also take a look at the |
Wow. The test suite needs a serious round of cleanup. |
@FrederikBolding I've tried reordering the operations a bit in the SendInternal. I've tried combining the error handling to one place, while returning quickly for successes. Likewise, I now only consider responses with JSON as being successfull, as iirc we never return streams of stuff (The images are links to elsewhere). How does this look? |
Looks good! 😄 - With regards to testing I've attempted to clean up and fix a bunch of tests in #253 |
So far I've added exceptions for errors occurring in the RestClient layer. I've also added some tweaks for handling nulls in case of not founds, as well as a property to determine whether or not to throw exceptions when items aren't found.
I'm not done testing yet and would like some feedback on how to handle nulls as well as any other errors that I haven't considered yet.
In relation to issues: #182 #194 #235 #270