If I remember correctly, the message in your point 1) will only display if you do not have a custom page.... it overrides the default plain http error code text that web2py produces.
As to point 2) which may help you solve point 1) as well, I think you may need to use dynamic error pages.
For example, I have a controller called 'errors' that looks like this:
def error_404():
""" Dynamic 404 error page... receives
<Storage {'ticket': 'None',
'code': '404',
'request_url': '/the_url',
'requested_uri': 'the_full_uri'}>
"""
# Set response code to original error, otherwise it will be 302.
response.status = 404
is_property = '/propert' in request.vars.requested_uri.lower()
if is_property:
return response.render('errors/error_404_property.html', {}) # This is an ordinary view so pass variables in the dict as 2nd parameter.
else:
return response.render('errors/error_404_general.html', {})
Then I have two (normal) views in /applications/myapp/views/errors that correspond to the two views used above with different messages.
In routes.py:
routes_onerror = [
(r'*/400', r'/static/400.html'), # bad request
(r'*/403', r'/static/403.html'), # forbidden
(r'*/404', r'/errors/error_404'), # DYNAMIC - not found page
]
Having said all that, to return json on 400 error, I would test whether this works for you:
No views need to be created. In the errors controller, in the function you create, just return dict(error='my err message') In routes_onerror, try: ('app/400', '/app/default/custom_400.json'). Web2py's automatic json conversion should kick in and return the json if all goes well. Remember to restart your app each time you change anything in routes.py.
HTH,