Wednesday, August 31, 2011

Example #14 - Error handling

For a real app, you'll need to do some error checking and handling on your API requests.  This example shows one approach.

You need to check that the urlopen() call worked.  If it didn't, you either couldn't reach the web site or you had a bad service request.  Reasons you might not reach the web site include:  you misspelled the data portal URL, the portal is down, or you aren't connected to the network.  A bad service request would typically be you misspelled it or (rare but I suppose it could happen) that service is down.

We'll use Python's exception for the urllib2 urlopen() function.  urlopen() sends back errors by raising exceptions and providing us some details to start the debugging.  You'll see we will try the urlopen() and check for two different exceptions:  HTTPError and URLError.

HTTPError tells us something is wrong with what we sent the server.  The server got the request but had problems with it.  You can find details on the various error codes about halfway down on this Python reference page.  There's a table of the values and a link to more info.

URLError tells us something is wrong with the URL we are using.  The server didn't get the request.  It didn't get that far.

Example Code
Four snippets are provided:  the first without error and the next three have different sorts of errors in the API request.

from urllib2 import urlopen, URLError, HTTPError


# 1 - Good URL and service name
print '\nHere\'s what happens with a good URL.'
url = 'http://data.cityofchicago.org/api/views.json'
try:
   u = urlopen(url)
except HTTPError, e:
   print 'The server could not handle the request.'
   print 'Error code: ', e.code
except URLError, e:
   print 'We failed to reach a server.'
   print 'Reason: ', e.reason
else:
   print 'OK'


# 2 - Bad URL
print '\nHere\'s what happens with a bad URL.'
url = 'http://XYZ.cityofchicago.org/api/views.json'
try:
   u = urlopen(url)
except HTTPError, e:
   print 'The server could not handle the request.'
   print 'Error code: ', e.code
except URLError, e:
   print 'We failed to reach a server.'
   print 'Reason: ', e.reason
else:
   print 'OK'


# 3 - Bad service name
print '\nHere\'s what happens with a good URL but bad API service name.'
url = 'http://data.cityofchicago.org/api/XYZ.json'
try:
   u = urlopen(url)
except HTTPError, e:
   print 'The server could not handle the request.'
   print 'Error code: ', e.code
except URLError, e:
   print 'We failed to reach a server.'
   print 'Reason: ', e.reason
else:
   print 'OK'


# 4 - Bad parameter
print '\nHere\'s what happens with a good URL and good API service name but bad parameter.'
url = 'http://data.cityofchicago.org/api/views.json?XYZ=2'
try:
   u = urlopen(url)
except HTTPError, e:
   print 'The server could not handle the request.'
   print 'Error code: ', e.code
except URLError, e:
   print 'We failed to reach a server.'
   print 'Reason: ', e.reason
else:
   print 'OK'

Explanations of each snippet
For each of the four snippets, here's their few lines of code, commentary, and output.

Good URL
# 1 - Good URL and service name
print '\nHere\'s what happens with a good URL.'
url = 'http://data.cityofchicago.org/api/views.json'
try:
   u = urlopen(url)
except HTTPError, e:
   print 'The server could not handle the request.'
   print 'Error code: ', e.code
except URLError, e:
   print 'We failed to reach a server.'
   print 'Reason: ', e.reason
else:
   print 'OK'
This is the normal case.  You've seen this URL several times before.  We're requesting all the views from the city of Chicago data portal.  We try to open the URL and since the output shows, OK, we skipped the exceptions - so, no errors.


Bad URL
# 2 - Bad URL
print '\nHere\'s what happens with a bad URL.'
url = 'http://XYZ.cityofchicago.org/api/views.json'
try:
   u = urlopen(url)
except HTTPError, e:
   print 'The server could not handle the request.'
   print 'Error code: ', e.code
except URLError, e:
   print 'We failed to reach a server.'
   print 'Reason: ', e.reason
else:
   print 'OK'
Here, the portal URL is wrong.  There is no web site at XYZ.cityofchicago.org.  Since this is a garbage URL, we end up with a URLError exception and we print out our own error message followed by the reason code from the Python urllib2 module.


Bad service name
# 3 - Bad service name
print '\nHere\'s what happens with a good URL but bad API service name.'
url = 'http://data.cityofchicago.org/api/XYZ.json'
try:
   u = urlopen(url)
except HTTPError, e:
   print 'The server could not handle the request.'
   print 'Error code: ', e.code
except URLError, e:
   print 'We failed to reach a server.'
   print 'Reason: ', e.reason
else:
   print 'OK
The URL is correct but the API service name is wrong.  There is no service called /api/XYZ.  When this runs, the Chicago portal gets the request.  However, it balks because it doesn't know what to do for /api/XYZ.  We have an HTTPError.  The server doesn't know what to do with the HTTP we sent.  We print out our error message followed by the error code from the Python urllib2 module.


Bad parameter
# 4 - Bad parameter
print '\nHere\'s what happens with a good URL and good API service name but bad parameter.'
url = 'http://data.cityofchicago.org/api/views.json?XYZ=2'
try:
   u = urlopen(url)
except HTTPError, e:
   print 'The server could not handle the request.'
   print 'Error code: ', e.code
except URLError, e:
   print 'We failed to reach a server.'
   print 'Reason: ', e.reason
else:
   print 'OK'
Last thing to check - what happens when you send a bad parameter in an API request?  Here, we send /api/views a name/value parameter of XYZ =2. XYZ is bogus.  As the output shows, this does not cause any exception.  The URL is good.  The service name is good.  As far as urllib2.urlopen() is concerned everything worked properly.  The service would need to send back an error message to us complaining about an unknown name.  The API just ignores the bad parameter.  (Want to see?  Add one more line to the code, print u.read(), and you will see in the output there's no indication of an error.)

Summary
You've seen how to write some error checking code and how the different sorts of errors are reported.  For a production app, this error checking is just a start.  You'd need to be more thorough and helpful to your users.   In addition, note that nothing will tell you about a bad parameter being sent to /api/views.

No comments:

Post a Comment