Search code examples
pythonvalidationwebdecoratorcherrypy

Cherrypy form validation


What do most cherrypy developers do for server side form validation? I've done a google but nothing jumped out as "the one way to do it".


Solution

  • You may write a tool and use it as decorator

    Or you can use my Validation decorator:

    import re
    from simplejson import JSONDecoder
    from simplejson.decoder import JSONDecodeError
    #from cherrypy._cperror import HTTPError
    from decimal import Decimal
    
    class ValidationException(Exception):
        pass 
    
    class ArgumentException(ValidationException):
        pass
    
    class MandatoryException(ValidationException):
        pass
    
    class DenialException(ValidationException):
        pass
    
    class ParameterTypeException(ValidationException):
        pass
    
    class ParameterValueException(ValidationException):
        pass
    
    def validate_parameters(args,kwargs,required_args=None
                                         ,deny_args=None
                                         ,requires=None
                                         ,deny=None
                                         ,deny_except=None
                                         ,types=None
                                         ,values=None
                                         ,filter=None
                                         ,json_decode=None
                                         ,defaults=None):
        """Validates input parameters of a callable
    
        @param required_arguments:   list of types, Required argument's types  & counts in order
        @param deny_args             string ,regex pattern, if any argument matches with the pattern, exception raises
        @param requires:             list of fields to check for mandatory
        @param deny:                 string ,regex pattern, if any parameter matches with the pattern, exception raises
        @param deny_except:          string ,regex pattern for excluding fields from deny
        @param types:                dict of key:name and value:type to check for types, if mismatch it will be raises exception
        @param values:               dict of key:name and value:regex pattern to check the values, & fire exception if mismatch
        @param filter:               string ,regex pattern of parameters to filter
        @param json_decode:          list of string: field to be decoded as json data    
        @param defaults:             dict of key:name and value:default_value
    
        @raise MandatoryException:           if any param in requires does not provided
        @raise ArgumentException:            if arguments are invalid , short or mismatch type.
        @raise DenialException:              if found param in deny list
        @raise ParameterTypeException:       if parameter types invalid
        @raise ParameterValueException:      if values are not in correct format
        """
    
    
        #check deny arguments
        if deny_args:
            for arg in args: 
                if re.match(deny_args, arg):
                    raise DenialException('the argument %s was not allowed' % arg)
    
        #decoding json parameters
        if json_decode:
            decoder = JSONDecoder(parse_float=Decimal)
            def decode(d):
                try:
                    return decoder.decode(d)
                except (JSONDecodeError,TypeError):
                    return d
    
            if isinstance(json_decode,list):
                decoded_list =[(key,decode(kwargs[key])) for key in kwargs if key in json_decode]
            else:
                decoded_list =[(key,decode(kwargs[key])) for key in kwargs]
            kwargs.update(decoded_list) 
    
    
    
        #check required arguments
        if required_args: 
            try:
                new_args = list(args)
                for at in required_args:
                    index = required_args.index(at)
                    if not isinstance(new_args[index],at):
                        try:
                            #try to cast
                            new_args[index] = at(new_args[index])
                        except:
                            raise ParameterTypeException('argument at index:%s must be %s' % (required_args.index(at),at))
                args = tuple(new_args)
            except IndexError:
                raise ArgumentException('argument\'s length is too short')
    
        #check required parameters
        if requires: 
            for name in requires:
                if name not in kwargs:
                    raise MandatoryException('the parameter:"%s" is mandatory' % name)
    
    
        filtered_params = {}
        for param in kwargs:
    
            #checking requires
            if requires and param in requires and not kwargs[param]:
                raise MandatoryException('the parameter:"%s" is mandatory' % param)
    
            #checking for denial
            if deny and re.match(deny,param) and  (not deny_except or not re.match(deny_except, param)):
                raise DenialException('Parameter: %s was denied' % param)
    
            #value checking
            if values and param in values and not re.match(values[param], kwargs[param]):
                raise ParameterValueException('Parameter:%s does not meet value pattern: given value:%s' % (param,kwargs[param]))
    
            #checking for types
            if types and param in types:
                if kwargs[param] and not isinstance(kwargs[param],types[param]):
                    try:
                        #try to cast the type too needed type
                        kwargs[param] = types[param](kwargs[param])
                    except:
                        raise ParameterTypeException('Parameter:%s must be type:%s, given type:%s' % (param,types[param],type(kwargs[param])))
    
    
            #filtering parameters
            if not filter or not re.match(filter,param):
                filtered_params[param] = kwargs[param]
    
        #add defaults if not supplied
        if defaults:
            for param in defaults:
                if param not in filtered_params:
                    filtered_params[param] = defaults[param]
    
        return args,filtered_params
    
    
    def validate(**val_cfg):
        def validecorator(func):
            def wrapper(*args,**kwargs):
                    if len(func.func_code.co_varnames) and func.func_code.co_varnames[0] == 'self':
                        new_args,filtered_params = validate_parameters(args[1:], kwargs,**val_cfg)
                        return func(*(args[0],)+new_args,**filtered_params)
                    else:
                        new_args,filtered_params = validate_parameters(args, kwargs,**val_cfg)
                        return func(*new_args,**filtered_params)
                #calling the callable!
    
            return wrapper
        return validecorator
    
    validate.__doc__ = validate_parameters.__doc__
    

    Example:

    @validate(types={'name':str,age:int},values={'name':'[a-zA-Z]{5,50}',age:'\d{1,2}'})
    def my_controller(name=None,age=None):
        # process data
        return render(\\some template\\)
    

    may help!

    EDIT 1

    Take a look at this package