Web Form Error Handling Techniques

S

Sean Abrahams

The following is a reprint of a message I sent to the tutor list a long
time ago, that I haven't gotten around to discussing with anyone else
and failed to hear a reply on the tutor list. Hoping someone here may
want to have some dialog.

--

I'm learning to write unit tests and am trying to write them for a web
application I'm working on.

I'm currently writing a test that is to purposefully fail by passing
invalid data to a function. However this brought me to thinking about
how to handle errors.

Let me set up a hypothetical scenario.

I have a web form with name and age as its two fields. When a person
enters his/her information and submits the form, I take the two fields
and create a dict that contains {'name' : 'Foo', 'age' : '82'}.

I pass this information to a function called, insertData which
looks something like this:

def insertData(data):

# test data for validity
if not data['name'].isalpha():
raise InvalidDataError(data['name'], "Name contains non-alpha
characters")
if not data['age'].isdigit():
raise InvalidDataError(data['age'], "Age contains non-digit
characters")

sql = """
INSERT INTO people (name, age) VALUES ('%s', '%s')
""" % (data['name'], data['age'])

executeSQL(sql)

I should first check to see if the data is valid, meaning that the
name contains only alpha characters and the age only containing
numeric characters.

If I raise an exception, how would one handle the reprinting of the
web form with a red * next to the field that contains invalid data? If
one field is in error, I can see that upon receiving an exception you
can have your code just reprint the web form with the red star, but
what about when both fields are in error, do you have the exception
create a list which then your code checks to see if it exists and then
loops through it to know what fields are in error?

And then again, perhaps I'm completely wrong and am going about this
in an amateur manner.

I realize this is more of a style question, but that's what I'm
interested in discussing.

Thanks,
--Sean
 
S

Samuel Walters

| Sean Abrahams said |
def insertData(data):

# test data for validity
if not data['name'].isalpha():
raise InvalidDataError(data['name'], "Name contains non-alpha
characters")
if not data['age'].isdigit():
raise InvalidDataError(data['age'], "Age contains non-digit
characters")

sql = """
INSERT INTO people (name, age) VALUES ('%s', '%s')
""" % (data['name'], data['age'])

executeSQL(sql)

I should first check to see if the data is valid, meaning that the
name contains only alpha characters and the age only containing
numeric characters.

If I raise an exception, how would one handle the reprinting of the
web form with a red * next to the field that contains invalid data? If
one field is in error, I can see that upon receiving an exception you
can have your code just reprint the web form with the red star, but
what about when both fields are in error, do you have the exception
create a list which then your code checks to see if it exists and then
loops through it to know what fields are in error?

So, what you're saying is that you wish to have the exception pass
information on which, of each piece, of the data is invalid?

Test it all, and then raise an error based on *all* the data that is
invalid.

For instance:

------
class InvalidDataError(Exception):
"""InvalidDataError
Attributes: values - a list of tuples of the form:
(name, data, offense)
Where name is the name of the field causing problems
Data is the data the field contained.
Offense is a string describing the problem encountered.
"""
def __init__(self, value):
self.value = value

def __str__(self):
return str(value)


def insertData(data):

errors = []
if not data['name'].isalpha():
errors.append(('name', data['name'], "Name contains non-alpha characters"))
if not data['age'].isdigit():
errors.append(('age', data['age'], "Age contains non-digit characters"))

if len(errors):
raise InvalidDataError(errors)

sql = """
INSERT INTO people (name, age) VALUES ('%s', '%s')
""" % (data['name'], data['age'])

executeSQL(sql)


good={"name":"bob", "age":"12343"}
bad={"name":"bob#", "age":"14"}
worse={"name":"bob", "age":"ni!"}
ugly={"name":"v@$$#", "age":"ni!"}

def test(data):
try:
insertData(data)
except InvalidDataError, e:
print "Data: %s failed with errors:"%(str(data))
for x in e.value:
name, data, problem = x
print "Field \"%s\" had bad data \"%s\" and reported \"%s\" as the problem."%(name, data, problem)
print
else:
print "Good %s found to be A-Ok!"%(str(data))
print

test(good)
test(bad)
test(worse)
test(ugly)
------

Now, with that code, you can format your user response any way you want.

User defined exceptions can toss around any type of data.
See section 8.5 of the tutorial for examples:
http://www.python.org/doc/current/tut/node10.html

Also, one other test that needs to be performed is to check whether the
data dictionary passed to insertData contains each of the fields required.
One checks this with data.has_key('name'). If you get passed a bad piece
of data, then you can handle it gracefully. Personally, I would create a
different exception for that because the cause could be either programmer
error, or bad data being passed to the program.

Personally, though, I wouldn't wait until just before inserting the data
to check it's validity. I prefer to catch invalid data as *early* as
possible. When I'm writing CGI and other web scripts, the outline of my
program looks like this:

Check for the existence and validity of each piece of data.
If error exists:
Generate and return an error page to the user.
Else:
Handle the data and generate a response page for the user.

It's a good habit to check the validity of each piece of data as soon as
it's received. This lets you separate problems where the user sent you
bad data, and problems where you messed up the data along the way. If the
user passed you decent data, then the only error they should see is some
form of "Internal Server Error."

HTH

Sam Walters.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top