2010-05-11 8 views
10

Tengo dificultades para entender cómo debo diseñar el error al manejar partes de mi código. Hace poco hice una pregunta similar acerca de cómo debo ir sobre returning server error codes to the user, eg. 404 errors. Aprendí que debería manejar el error desde la parte actual de la aplicación; parece lo suficientemente simple.¿Cómo debo manejar los errores previstos? p.ej. "nombre de usuario ya existe"

Sin embargo, ¿qué debo hacer cuando no puedo manejar el error del enlace actual en la cadena? Por ejemplo, puedo tener una clase que se usa para administrar la autenticación. Uno de sus métodos podría ser createUser($username, $password). Editar: Este método devolverá una identificación de usuario o un objeto de usuario. Dentro de esa función, necesito determinar si el nombre de usuario ya existe. Si esto es cierto, ¿cómo debo alertar al código de llamada sobre esto? Devolver nulo en lugar de un objeto de usuario es una forma. ¿Pero cómo sé entonces qué causó el error?

¿Cómo debo manejar los errores de tal manera que el código de llamada pueda descubrir fácilmente qué causó el error? ¿Hay un patrón de diseño comúnmente utilizado para este tipo de situación?

Editar: Se me olvidó mencionar: Estoy usando PHP.


Resuelto: Mientras que muchas personas argumentan que las excepciones no deben utilizarse en esta situación, he llegado a la conclusión de que es la mejor solución.

En primer lugar, no existe una alternativa sencilla y elegante a las excepciones. (Reconozco que no es posible sin un sistema integrado en el lenguaje ... las excepciones ya están incorporadas.)

En segundo lugar, en respuesta a las "excepciones solo se deben usar para situaciones excepcionales, y este no es uno "argumento: "When I call getFoo(), I actually expect to get a Foo. If I don't get it, it's by definition an exceptional event." (a través, pkainulainen)

Respuesta

3

Hay algunos patrones comunes:

1.   lanzar una exception.

2.   Devuelve NULL o FALSE y establece una contraseña aprobada en referencia a un error. P.ej.

function createUser($user, $password, &$error) 
{ 
     //... 
     // You could use any type of object here. This is just a simple example. 
     if(uniqueKeyFailure) 
     { 
      $error = new UserAlreadyExists(); 
      return NULL; 
     } 
     //.. 
} 

Se podría llamar como:

$userCreateError = NULL; 
$res = createUser($user, $pass, $userCreateError); 
if($res === NULL) 
{ 
    // do something with $userCreateError 
} 

3.   NULL Retorno o FALSO y proporcionan un último error (por ejemplo curl_error) GET.

Recomendaría 1 o 2. La razón principal por la que las personas evitan las excepciones para los errores "excepcionales" como la entrada del usuario es el rendimiento. Hay una buena cantidad de discusión sobre esto, como Performance of try-catch in php.

No recomiendo 3 ya que no es reentrante.

+0

¿Podría ampliar lo que quiere decir con "establecer un objeto de error", por favor? ¡Gracias! –

+1

De estos tres, las excepciones parecen casi hechas a medida para tratar con el tipo de escenarios descritos en la pregunta. Puede escoger y elegir cómo manejar cada evento excepcional según la clase y el mensaje de la excepción. – AndreiM

+0

@tehblanx - Creo que no estoy de acuerdo. El 'createUser' debería devolver' true' en success y 'false' en caso contrario. Idealmente, todo esto se manejará en una clase y la clase misma puede manejar los problemas que surjan de un registro fallido. – thetaiko

0

Puede devolver enteros definidos en un archivo de encabezado global con nombres como DUPLICATE_USER o INVALID_PASSWORD o lo que sea apropiado para su uso. Luego, la función de llamada puede verificar fácilmente estos códigos de retorno contra las declaraciones de encabezado globales para determinar qué tipo de error ocurrió.

+0

Olvidé mencionar que el método ya estará devolviendo información (identificación de usuario o un objeto de usuario) por lo que no puedo devolver los códigos de error. –

+0

¿Por qué no simplemente hacer que el método devuelva el código de error? – user97410

+0

Porque una vez que se ha creado el usuario, me gustaría poder seguir/utilizar la información sobre el nuevo usuario. Si el método devuelve un objeto de usuario, entonces puedo seguir mi camino. Si no es así, entonces tendría que ejecutar una segunda consulta para encontrar al usuario por su nombre de usuario. Aunque es fácil en esta situación porque el nombre de usuario será único, hay muchos ejemplos en los que necesitaría el ID para identificar la información, por ej. almacenando registros. –

0

esto tendría que hacerse en forma siguiente -

crear un objeto de error con códigos de error en las mismas, texto de error y el método que arrojó el error.

Devuelve el objeto de error.

que podría lanzar excepciones volver pero las excepciones son caros

EDITAR - Sobre la base de edición de la OP. En ese caso, tendrías que devolver un objeto de excepción.

Cree una clase de excepción con código de error, descripción del error, nombre de clase, nombre del método. Lanza la excepción de vuelta.

-2

Se espera que compruebe si el nombre de usuario existe ANTES de llamar al método createUser(). Si no lo hace, o sucede algo entre el momento en que lo haya marcado y lo haya llamado createUser(), lanzará una excepción. No es tan caro como se supone que es.

¿Tiene un costo? Sí. ¿Es lo suficientemente grande como para importar? Probablemente no.

+1

No se trata de gastos, se trata de "un usuario con ese nombre ya existe" no es realmente una condición excepcional. De hecho, se prevé que esto sucederá. Lanzar una excepción es abuso del manejo de excepciones. – timdev

+0

¡Eso es una tontería semántica! con ese tipo de razonamiento puede explicar cualquier error como "esperado" – hop

+0

Si puede anticipar que puede suceder, entonces verifique ese caso, antes de intentar crear el usuario, con isUserNameTaken() o algo así. –

1

A menudo me ocupo de este tipo de cosas al pegar mi validación dentro de algún objeto de dominio (como un usuario).

Por ejemplo:

<?PHP 
$user->name = 'Joe'; 
$user->password = 'secret'; 

//try to save the user. 
if (! $user->save()){ 
    $errors = $user->getMessages(); 
    // ... do something with error messages 
}else{ 
    //the user object set it's own ID. 
    $id = $user->id; 
} 

Ahora, muchas cosas se abstrae detrás del usuario. Puede haber un universo entero de objetos allí, pero eso depende de ti.

El problema más importante aquí es: ¿Por qué en el mundo tendría una clase de autenticación crear un usuario? La creación de objetos de usuario probablemente esté fuera del alcance de la Autenticación.

Podría ser conveniente contar con algún método como authenticate($username,$password)retorno un objeto de tipo 'usuario' (que representa al usuario que acaba de autenticado), pero incluso que es un poco desordenado.

considerar en cambio algo como:

<?PHP 
$auth = new AuthService(); 
$u = new User(); 
$u->username='Joe'; 
$u->password='secret'; 

$authResult = $auth->authenticate($user); 

if ($authResult->success){ 
    //$user has properties set as a side effect. 
}else{ 
    //find out why it failed. 
    $errors = $authResult->getErrors(); 
} 

Por supuesto, esto requiere que se defina una especie de clase de resultado-valor por AuthService :: authenticate() para llenar y devolver. Pero al menos no termina con métodos que a veces devuelven booleanos y algunas veces devuelven objetos, etc.

0

Si controla el tipo de devolución del método, también puede cambiar su firma para devolver algo así como un objeto OperationResult que tendría una propiedad ErrorDescription y una propiedad UserID o User object.

+0

Devolver elementos múltiples, algo sin relación, de una función suena como un olor a código. –

0

Manera más fácil: devuelve NULL si se aprueba la validación y el mensaje de error si falla. Evita la necesidad de hacer clases de error o excepciones. También lo mejor para el rendimiento.

por ejemplo.

function numeric($input) { 
    if (!is_numeric($input)) return 'Must be numeric.'; 
    return NULL; 
} 

function usernameavailable($input) { 
    //query database.... 
    if ($result) return 'Username already taken, please choose another one.'; 
    return NULL; 
} 

puede utilizar las funciones con parámetros así:

function length($input, $min, $max) { 
    if (strlen($input) < $min) return "Must be longer than $min characters."; 
    if (strlen($input) > $max) return "Must be shorter than $max characters."; 
    return NULL; 
} 

Validación de la cada elemento en la forma entera es entonces extremadamente fácil. Simplemente recorra todos los elementos, llame a todas las funciones de validación para ese elemento y recopile las cadenas de error, si las hubiera. Si no hay cadenas de error, entonces toda la entrada del usuario es válida.

Desaconsejaré encarecidamente las sugerencias hechas por una de las otras respuestas. La entrada de usuario no válida no es excepcional y no requiere una excepción o un error. Es simplemente un caso de uso esperado que debe tratarse de la manera más simple posible.

Con este método, puede crear una clase Form fácilmente y simplemente crear instancias con los argumentos que son las funciones de validación. También puede expandir esto para incluir la validación de AJAX enviando solicitudes asíncronas a las mismas funciones de validación de PHP.

ejemplo de mi marco:

$Form = new AjaxForm(); 
$Form->add(new TextBox('Username', 'usernameavailable|length[3,12]')); 
$Form->add(new SubmitButton()); 

Con sólo tres líneas que he creado una forma totalmente funcional, con tanto del lado del cliente y la validación del lado del servidor. Por supuesto, todos los detalles dependen de usted, pero sigo creyendo que la entrada de un usuario no válido debe esperarse y no ser excepcional.

Cuestiones relacionadas