Проверкой занимаются так называемые Валидаторы(которые являются лишь частью всего процесса проверки данных). В статье Серверная валидация пользовательских данных приводится интересный вариант реализации валидатора, но есть несколько нюансов в виде локализации сообщений и самого формата ошибок.
Рассмотрим сначала формат ошибок.
Предлагаемый подход состоит в том, чтобы метод валидатора, проверяющий данные, возвращал коллекцию(массив, список и т.д.) строк вместо булевых значений или бросания исключений. Такой формат будет более гибким и информативным.
Приведу пример на Java:
import java.util.ArrayList; import java.util.Collection; import java.util.regex.Matcher; import java.util.regex.Pattern; public interface ValidateUser { String OK = "OK:"; String FAIL= "FAIL:"; Collection<String> apply(String username, String password, String email); default Collection<String> passwordValidate(String password){ var result = new ArrayList<String>(1); int size = password.trim().length(); if(size < 3 || size > 20) result.add("Password error:too short value or too long name. Password must be greater or 3 characters and smaller then 20 simbols."); return result; } default Collection<String> usernameValidate(String name){ var result = new ArrayList<String>(1); int size = name.trim().length(); if(size < 3 || size > 30) result.add("Username error:too short or too long name. Name must be greater or 3 characters and smaller then 30 simbols."); return result; } default Collection<String> emailValidate(String email){ var result = new ArrayList<String>(1); String regex = "^[\\w!#$%&'*+/=?`{|}~^-]+(?:\\.[\\w!#$%&'*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(email); if (!matcher.find()) result.add("Email error:" + email + " is not valid"); return result; } class Default implements ValidateUser{ @Override public Collection<String> apply(String username, String password, String email) { var errors = passwordValidate(password.trim()); errors.addAll(usernameValidate(username.trim())); errors.addAll(emailValidate(email.trim())); return errors; } } }
Как здесь видно все методы возвращают коллекцию строк.
Пример Unit теста:
@Test void validateUserTest() { var validate = new ValidateUser2.Default(); var result = validate.apply("aaa", "qwe", "aaa@mail.ru"); assertTrue(result.isEmpty()); result = validate.apply("aaa", "qwe", ""); assertFalse(result.isEmpty()); assertEquals(1, result.size()); result = validate.apply("aa", "qwe", "aaa@mail.ru"); assertFalse(result.isEmpty()); assertEquals(1, result.size()); result = validate.apply("aaa", "qwe", "@mail.qweqwe"); assertFalse(result.isEmpty()); assertEquals(1, result.size()); result = validate.apply("aa", "qw", ""); assertFalse(result.isEmpty()); assertEquals(3, result.size()); }
А теперь рассмотрим локализацию сообщений ошибок.
Пример снова на Java:
public interface LocalizedValidation { String OK = "OK:"; String FAIL= "FAIL:"; Collection<String> apply(String username, String password, String email, Locale locale); default Collection<String> passwordValidate(String password, ResourceBundle bundle){ var result = new ArrayList<String>(1); int size = password.trim().length(); if(size < 3 || size > 20) result.add(bundle.getString("password")); return result; } default Collection<String> usernameValidate(String name, ResourceBundle bundle){ var result = new ArrayList<String>(1); int size = name.trim().length(); if(size < 3 || size > 30) result.add(bundle.getString("username")); return result; } default Collection<String> emailValidate(String email, ResourceBundle bundle){ var result = new ArrayList<String>(1); String regex = "^[\\w!#$%&'*+/=?`{|}~^-]+(?:\\.[\\w!#$%&'*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(email); if (!matcher.find()) result.add(bundle.getString("username")+email); return result; } class Default implements LocalizedValidation{ @Override public Collection<String> apply(String username, String password, String email, Locale locale) { ResourceBundle bundle = ResourceBundle.getBundle("errors", locale); var errors = passwordValidate(password.trim(), bundle); errors.addAll(usernameValidate(username.trim(), bundle)); errors.addAll(emailValidate(email.trim(), bundle)); return errors; } } }
@Test void localizedUserTest() { var validate = new LocalizedValidation.Default(); var result = validate.apply("aaa", "qwe", "aaa@mail.ru", Locale.ENGLISH); assertTrue(result.isEmpty()); result = validate.apply("aaa", "qwe", "", Locale.ENGLISH); assertFalse(result.isEmpty()); assertEquals(1, result.size()); System.out.println(result.iterator().next()); result = validate.apply("aaa", "qwe", "", new Locale("ru")); assertFalse(result.isEmpty()); assertEquals(1, result.size()); System.out.println(result.iterator().next()); }
Файлы локализаций лежат в src/main/resources.
errors_ru.properties:
mail=Email ошибка: не верный Email: username=Ошибка имени пользователя: слишком короткое или длинное имя. password=Ошибка в длине пароля: пароль слишком длинный.
errors.properties:
mail=Email error: is not valid: username=Username error:too short or too long name. Name must be greater or 3 characters and smaller then 30 simbols. password=Password error:too short value or too long name. Password must be greater or 3 characters and smaller then 20 simbols.
Надеюсь, что и другие программисты, пишушие на своих языках, найдут такой подход практичным и удобным и смогут его применить у себя.
P.S.
Вариант метода аутентификации пользователя и возврат строки из метода, которая может содержать в себе как данные пользователя(будь то Id пользователя, XML или JSON) так и данные об ошибках и парситься на стороне клиента:
public String authUser(String username, String password, String email, Locale locale) { var validate = new LocalizedValidation.Default(); var errors = validate.apply("aaa", "qwe", "aaa@mail.ru", locale); if(!errors.isEmpty()) { return LocalizedValidation.FAIL+errors.stream().collect(Collectors.joining("<br/>")); }else { try { var user = new User.Default(0L, username.trim(), email.trim(), new Encrypt().apply(password.trim()), "").create(dataSource); return LocalizedValidation.OK+user.toString();// or LocalizedValidation.Ok+user.id(); //or LocalizedValidation.Ok+user.toJson(); } catch (RuntimeException e) { return FAIL+e.getMessage(); } } }
Есть, конечно, замечательная статья на хабре про валидацию в Java, но подход который там предлагается может использоваться далеко не во всех случаях и основан на аннотациях и исключениях.
В окончание статьи пару полезных ссылок про тестирование:
ссылка на оригинал статьи https://habr.com/ru/post/487838/
Добавить комментарий