Как я должен обработать ожидаемые погрешности? например, “имя пользователя уже существует”

Я изо всех сил пытаюсь понять, как я должен разработать части обработки ошибок своего кода. Я недавно задал подобный вопрос о том, как я должен пойти о возврате кодов ошибок сервера пользователю, например, 404 ошибок. Я узнал, что должен обработать ошибку из текущей части приложения; достаточно простой seem's.

Однако, что я должен сделать, когда я не могу обработать ошибку от текущего звена цепи? Например, у меня может быть класс, который используется для управления аутентификацией. Один из него - методы, мог быть createUser($username, $password).Править: Этот метод возвратит идентификатор пользователя или пользовательский объект. В той функции я должен определить, существует ли имя пользователя уже. Если это верно, как я должен предупредить код вызова об этом? Возврат пустого указателя вместо пользовательского объекта является одним путем. Но как я затем знаю то, что вызвало ошибку?

Как я должен обработать ошибки таким способом, которым код вызова может легко узнать то, что вызвало ошибку? Существует ли шаблон разработки, наиболее часто используемый для этого вида ситуации?

Править: Я забыл упоминать: Я использую PHP.


Решенный: В то время как многие люди утверждают, что исключения не должны использоваться в этой ситуации, я пришел к выводу, что это - лучшее решение.

Во-первых, нет никакой простой, изящной альтернативы исключениям. (Я повторно подставляю это, это не возможно без системы, встроенной в язык... исключения уже встроены.)

Во-вторых, в ответ на "исключения должен только использоваться для исключительных ситуаций, и это не один" аргумент: "Когда я назову getFoo (), я на самом деле ожидаю получать Нечто. Если я не получаю его, это - по определению исключительное событие". (через, pkainulainen)

10
задан Community 23 May 2017 в 12:18
поделиться

7 ответов

Есть несколько общих шаблонов:

1. Выбросить исключение.

2. Вернуть NULL или FALSE и установить переданную ссылку на ошибку. Например,

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;
      }
      //..
}

Это может быть вызвано так:

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

3. Вернуть NULL или FALSE и передать ссылку на последнюю ошибку (например, curl_error).

Я бы рекомендовал 1 или 2. Основная причина, по которой люди избегают исключений для "неисключительных" ошибок, таких как пользовательский ввод, - это производительность. Существует достаточно много дискуссий по этому поводу, например Производительность try-catch в php.

Я не рекомендую 3, поскольку он не является реентерабельным.

3
ответ дан 4 December 2019 в 04:36
поделиться

Я часто обрабатываю такие вещи, вставляя свою проверку в какой-то объект домена (например, пользователь).

Например:

<?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;
}

Теперь за пользователем абстрагируется много вещей. Там может быть целая вселенная объектов, но это от вас.

Более серьезная проблема здесь заключается в том, почему в мире у вас есть класс аутентификации создать пользователя? Создание пользовательских объектов, вероятно, выходит за рамки аутентификации.

Возможно, было бы разумно иметь какой-то метод типа authenticate($username,$password) return объект типа 'user' (представляющий пользователя, который только что аутентифицировался), но даже это немного беспорядочно.

Вместо этого рассмотрим что-то вроде:

<?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();
}

Конечно, для этого требуется определить какой-то результат.класс value для AuthService::authenticate() для заполнения и возврата. Но, по крайней мере, вы не получите методы, которые иногда возвращают логические значения, а иногда возвращают объекты и т. Д.

1
ответ дан 4 December 2019 в 04:36
поделиться

Если вы управляете типом возврата метода, вы также можете изменить его подпись, чтобы вернуть что-то вроде объекта OperationResult, который будет иметь свойство ErrorDescription и свойство объекта UserID или User.

0
ответ дан 4 December 2019 в 04:36
поделиться

Самый простой способ: вернуть NULL, если проверка прошло, и сообщение об ошибке, если это не удалось. Предотвращает необходимость создавать классы ошибок или исключения. Также лучший по производительности.

например.

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;
}

Вы также можете использовать функции с параметрами:

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;
}

Тогда проверка каждого элемента во всей форме становится чрезвычайно простой. Просто переберите все элементы, вызовите все функции проверки для этого элемента и соберите строки ошибок, если таковые имеются. Если строки с ошибками отсутствуют, значит, все данные, введенные пользователем, действительны.

Я бы категорически не одобрял предложения, сделанные одним из других ответов. Недействительный ввод пользователя не является исключительным и не требует исключения или ошибки. Это просто ожидаемый вариант использования, с которым нужно работать как можно проще.

С помощью этого метода вы можете довольно легко создать класс Form и просто создавать экземпляры элементов с аргументами, являющимися функциями проверки. Вы также можете расширить это, чтобы включить проверку AJAX, отправляющую асинхронные запросы к тем же функциям проверки PHP.

Пример из моей структуры:

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

Всего с тремя строчками я создал полностью функциональную форму с проверкой как на стороне клиента, так и на стороне сервера. Конечно, все детали зависят от вас, но я по-прежнему считаю, что недопустимый ввод пользователя следует ожидать, а не быть исключительным.

0
ответ дан 4 December 2019 в 04:36
поделиться

Это должно быть сделано следующим образом -

Создайте объект ошибки с кодами ошибок в них, текстом ошибки и методом, который выбросил ошибку.

Передайте объект ошибки обратно.

Вы можете бросать исключения обратно, но исключения стоят дорого

EDIT - Основываясь на правке ОП. В этом случае вам придется бросать объект исключения обратно.

Создайте класс исключения с кодом ошибки, описанием ошибки, именем класса, именем метода. Бросьте исключение обратно.

0
ответ дан 4 December 2019 в 04:36
поделиться

Вы можете возвращать целые числа, которые определены в глобальном файле заголовка с такими именами, как DUPLICATE_USER или INVALID_PASSWORD, или любым другим, подходящим для вашего использования. Затем вызывающая функция может легко сверить эти коды возврата с объявлениями глобального заголовка, чтобы определить, какой тип ошибки произошел.

0
ответ дан 4 December 2019 в 04:36
поделиться

Вы должны проверить, существует ли имя пользователя ПЕРЕД вызовом метода createUser(). Если вы этого не делаете или что-то происходит между временем проверки и вызова createUser(), вы создаете исключение. Это далеко не так дорого, как это представляется.

Есть ли у этого стоимость? Да. Достаточно ли он велик, чтобы иметь значение? Скорее всего, нет.

-2
ответ дан 4 December 2019 в 04:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: