问题
Standard libraries (xmlrpclib
+SimpleXMLRPCServer
in Python 2 and xmlrpc.server
in Python 3) report all errors (including usage errors) as python exceptions which is not suitable for public services: exception strings are often not easy understandable without python knowledge and might expose some sensitive information. It's not hard to fix this, but I prefer to avoid reinventing the wheel. Is there a third party library with better error reporting? I'm interested in good fault messages for all usage errors and hiding internals when reporting internal errors (this is better done with logging).
xmlrpclib
already have the constants for such errors: NOT_WELLFORMED_ERROR
, UNSUPPORTED_ENCODING
, INVALID_ENCODING_CHAR
, INVALID_XMLRPC
, METHOD_NOT_FOUND
, INVALID_METHOD_PARAMS
, INTERNAL_ERROR
.
回答1:
I don't think you have a library specific problem. When using any library or framework you typically want to trap all errors, log them somewhere, and throw up "Oops, we're having problems. You may want to contact us at x@x.com with error number 100 and tell us what you did." So wrap your failable entry points in try/catches, create a generic logger and off you go...
回答2:
It look like there is no ready library with my requirements, so a ended up with own implementation:
class ApplicationError(Fault):
def __init__(self, exc_info):
Fault.__init__(self, xmlrpclib.APPLICATION_ERROR,
u'Application internal error')
class NotWellformedError(Fault):
def __init__(self, exc):
Fault.__init__(self, xmlrpclib.NOT_WELLFORMED_ERROR, str(exc))
class UnsupportedEncoding(Fault):
def __init__(self, exc):
Fault.__init__(self, xmlrpclib.UNSUPPORTED_ENCODING, str(exc))
# XXX INVALID_ENCODING_CHAR is masked by xmlrpclib, so the error code will be
# INVALID_XMLRPC.
class InvalidRequest(Fault):
def __init__(self, message):
ault.__init__(self, xmlrpclib.INVALID_XMLRPC, message)
class MethodNotFound(Fault):
def __init__(self, name):
Fault.__init__(self, xmlrpclib.METHOD_NOT_FOUND,
u'Method %r is not supported' % name)
class WrongMethodUsage(Fault):
def __init__(self, message):
Fault.__init__(self, xmlrpclib.INVALID_METHOD_PARAMS, message)
class WrongType(Fault):
def __init__(self, arg_name, type_name):
Fault.__init__(self, xmlrpclib.INVALID_METHOD_PARAMS,
u'Parameter %s must be %s' % (arg_name, type_name))
class XMLRPCDispatcher(SimpleXMLRPCDispatcher, XMLRPCDocGenerator):
server_name = server_title = 'Personalization center RPC interface'
server_documentation = 'Available methods'
def __init__(self, methods):
SimpleXMLRPCDispatcher.__init__(self, allow_none=True, encoding=None)
self.register_instance(methods)
self.register_multicall_functions()
#self.register_introspection_functions()
def _dispatch(self, method_name, args):
if self.funcs.has_key(method_name):
method = self.funcs[method_name]
else:
method = self.instance._getMethod(method_name)
arg_names, args_name, kwargs_name, defaults = \
inspect.getargspec(method)
assert arg_names[0]=='self'
arg_names = arg_names[1:]
n_args = len(args)
if not (args_name or defaults):
if n_args!=len(arg_names):
raise WrongMethodUsage(
u'Method %s takes exactly %d parameters (%d given)' % \
(method_name, len(arg_names), n_args))
else:
min_args = len(arg_names)-len(defaults)
if len(args)<min_args:
raise WrongMethodUsage(
u'Method %s requires at least %d parameters (%d given)' % \
(method_name, min_args, n_args))
if not args_name and n_args>len(arg_names):
raise WrongMethodUsage(
u'Method %s requires at most %d parameters (%d given)' % \
(method_name, len(arg_names), n_args))
try:
return method(*args)
except Fault:
raise
except:
logger.exception('Application internal error for %s%r',
method_name, args)
raise ApplicationError(sys.exc_info())
def dispatch(self, data):
try:
try:
args, method_name = xmlrpclib.loads(data)
except ExpatError, exc:
raise NotWellformedError(exc)
except LookupError, exc:
raise UnsupportedEncoding(exc)
except xmlrpclib.ResponseError:
raise InvalidRequest('Request structure is invalid')
method_name = method_name.encode('ascii', 'replace')
result = self._dispatch(method_name, args)
except Fault, exc:
logger.warning('Fault %s: %s', exc.faultCode, exc.faultString)
return xmlrpclib.dumps(exc)
else:
try:
return xmlrpclib.dumps((result,), methodresponse=1)
except:
logger.exception('Application internal error when marshalling'\
' result for %s%r', method_name, args)
return xmlrpclib.dumps(ApplicationError(sys.exc_info()))
class InterfaceMethods:
def _getMethod(self, name):
if name.startswith('_'):
raise MethodNotFound(name)
try:
method = getattr(self, name)
except AttributeError:
raise MethodNotFound(name)
if not inspect.ismethod(method):
raise MethodNotFound(name)
return method
来源:https://stackoverflow.com/questions/1571598/xml-rpc-server-with-better-error-reporting