XML-RPC server with better error reporting

泪湿孤枕 提交于 2019-12-24 10:45:22

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!