Суть анонимов достаточно проста, это неопределённый пользователь, однако он имеет права производить такие операции, как комментирование и создание анонимных жалоб. Однако в действительности, они не столь анонимны относительно внутренней структуры системы.
Каждый аноним, заходя первый раз передаёт свой IP адрес, что не мало важно, и даёт информацию о местоположении. Эта информация может быть использована в целях персонализации вывода страниц. Например, показывая жалобы или новости из своего района, ближайшие компании, статистика, плотность пользователей и тому подобную информацию.
Анонимы записываются как самые настоящие пользователи, в таблицу пользователей, однако с маркером в виде колонки IsAnonym. Такая запись является первой стадией, и её задача есть поддержание последовательности действий будущего зарегистрированного пользователя. Конечно, такой подход не лишён недостатков - меняя браузер, удаляя cookies, производя вход другим аккаунтом, отметка анонима будет очищена и создана заново. Следовательно, введённая информация так и останется ссылающейся на пользователя анонима. В случае успешной регистрации, UserId анонима уже использованная при создании комментариев, будет преобразована в нового пользователя, не меняя UserId.
Реализация механизма работы, такого анонима, производится в ручную. В asp.net 2.0, был включёна реализация AnonymousModule, однако он работает с более простой схемой, когда маркер аноним остаётся постоянно[почему?]. Проблема начинается в момент выхода из аккаунта зарегистрированного пользователя, так как UserId анонима и пользователя совпадают, то при выходе нужно удалять и анонимную, и пользовательскую cookie, а встроенный механизм этого не позволяет. Поэтому для нашей схемы реализуется свой модуль анонимных пользователей.
partial class User { public static User Current { get { return HttpContext.Current.User.Identity.IsAuthenticated ? User.Get(HttpContext.Current.User.Identity.Name) : RegisterAnonym(); } } public static User RegisterAnonym() { return RegisterAnonym(Anonymous.Get()); } public static User RegisterAnonym(Guid anonymId) { User user = Rep<User>.GetById(anonymId); if (user == null) { user = new User { UserId = anonymId, IsActivated = false, IsDeleted = false, IsGuest = true }; Ctx.Model.Users.InsertOnSubmit(user); } Ctx.Model.SubmitChanges(); Ctx.User = user; return user; } |
Рисунок 2.17 – Регистрация анонима и текущий пользователь
Как видно, текущего пользователь получают через статическое свойство User.Current. Это свойство не кеширует пользователя, загружая его, либо анонима через RegisterAnonym. Он производит два действия: как добавление анонима, так и загрузку по UserId в случае не существования и в результате в любом случае выдаёт некого пользователя.
public class Anonymous { public static Guid Get() { HttpCookie cookie = HttpContext.Current.Request.Cookies[CookieName]; try { return cookie == null ? New() : new Guid(cookie.Value); } catch(Exception exc) { return New(); } } public static Guid New() { Guid anonymId = Guid.NewGuid(); HttpCookie cookie = HttpContext.Current.Response.Cookies[CookieName]; cookie.Expires = DateTime.Now.AddDays(30); cookie.Value = anonymId.ToString(); return anonymId; } public static readonly string CookieName = "Yabeda.AnonId"; } |
Рисунок 2.18 – Класс Anonym
Этот статический класс отвечает за работу с cookie: запрос идентификатора анонима, соответствующего идентификатору UserId, генерация и установка на клиент, если cookie не найден. Сгенерированный Id затем будет использован в RegisterAnonym, как видно на прошлом изображении.
Регистрация – это процесс перехода состояния пользователя в более постоянную величину. Основные задачи достижимые при использовании регистрации: улучшенный уровень персонализации и более упрощённый ввод идентификационный информации, во время пользования системой. Для создателей, регистрация, прежде всего трассировка движения, и более простой и точный способ просмотра статистики.
Рисунок 2.19 – Процесс регистрации
Сверху диаграмма статусов, для процесса регистрации. Как видно процесс, состоит из этапа валидации информации, отсылки email и активации по ссылке из email. Достаточно стандартный процесс в веб-приложениях. Внешне работа формы регистрации описывается в пункте [веб ресурс – сценарий – регистрация]. Здесь же далее будет описан UserController и клиентские компоненты, использованные на странице регистрации выполняющие функциональность.
UserController – это управляющий элемент таких действий, как логин, логаут, регистрация, активизация и профайл пользователя. Как и задача любого mvc контроллера, он рассматривает присланные данные, наполняет в зависимости от них модель и пользуясь View отображает её. В случае с UserController, результатом некоторых действий есть json, тоесть он выступает как rest-сервис [ссылки на rest и json] вызываемый при помощи ajaj. В данном случае, регистрация и логин переносит валидацию в асинхронные вызовы, и на клиенте обрабатывает ответ. На сервере находиться входное действие Register и RegisterJson выполняющее функцию сервиса.
Register выглядит как самый простой Action:
[UserFilter(IsGuest = true)] public ActionResult Register() { ViewData.Model = new PageModel { Title = "Регистрация" }; return View(); } |
Рисунок 2.20 – Action регистрации
UserFilter указывает как видно, что доступ к регистрации имеет любой пользователь. Ниже листинг разметки отображаемой страницы:
<div class="register container"> <div class="inputs"> Имя: <input type="text" id="name" /> <br /> Email: <input type="text" id="email" /> <br /> Пароль: <input type="password" id="pass" /> <br /> <input type="button" class="submit" value="Регистрация" /> </div> <div class="validation"> <div class="email"> <span class="status failed"> </span> 1. Проверяем уникальность email <div class="result"></div> </div> <div class="pass"> <span class="status failed"> </span> 2. Проверяем качество пароля <div class="result"></div> </div> <div class="send"> <span class="status failed"> </span> 3. Отсылаем письмо на ваш email <div class="result"></div> </div> </div> </div> |
Рисунок 2.21 – Разметка для страницы регистрации
Слева вводимые поля – имя, email и пароль. Справа валидационная часть. Реализация достаточно гибкая и чистая маркировка.
.register .inputs, .register .validation { float: left; margin: 20px; padding: 10px; border: dotted 1px red; } .register .validation > div { margin: 20px; } .register .validation .result { font-size: small; margin-left: 40px; } .register .status { padding: 10px; margin: 5px; } .register .status.failed { background-color: red } .register .status.passed { background-color: green } |
Рисунок 2.22 – CSS блоков
Первые три отвечают за оформление двух блоков и относительное позиционирование друг к другу и элементов внутри. Status класс, выражает квадрат, метку выполненности каждой из валидаций. Указанные рядом failed или passed соответствует результату, который пришёл от RegisterJson.
$(function() { $('.register .submit').click(function() { var params = { name: $('.register .inputs #name').val(), email: $('.register .inputs #email').val(), pass: $('.register .inputs #pass').val() }; $.getJSON(Yabeda.Services.User.RegisterJson, params, function(json) { $('.register .validation .result').empty(); $.each(json, function(i, n) { $('.register .validation .' + i + ' .status') .removeClass(n.length == 0 ? 'failed' : 'passed') .addClass(n.length == 0 ? 'passed' : 'failed'); $.each(n, function(ir, r) { $('.register .validation .' + i + ' .result') .append('-').append(r).append('<br />'); }) }) if ($('.register .validation .failed').size() == 0) { setTimeout(function() { window.location = Yabeda.Services.User.Profile; }, 700); } }); }); }); |
Рисунок 2.23 – Код управления валидацией регистрационной информацией
На кнопку «Регистрация» устанавливается обработчик, который собирает параметры на проверку и передаёт их в RegisterJson. Если например отправить все пустые значение, то ответом Json будет строка снизу.
{"email":["Email введён неверно"], "pass":[], "send":["Без email, не могу послать письмо активации"]} |
Рисунок 2.24 – Пример присланного RegisterJson
Формат Json в данном случае выглядит как вид валидации, и массив ошибок соответствующий ему. В Csharp для сериализации используется следующий класс.
public class ValidationJson { public List<string> email = new List<string>(); public List<string> pass = new List<string>(); public List<string> send = new List<string>(); } |
Рисунок 2.25 – Версия ValidationJson
RegisterJson должен заполнить этот класс по мере валидации.
public ActionResult RegisterJson(string name, string email, string pass) { var validation = new Models.User.ValidationJson(); // 1. Check similar if (!Emails.Check(email)){ validation.email.Add("Email введён неверно"); } else { if (Data.User.Get(email) != null) { validation.email.Add("Такой пользователь существует"); } } // 2. Send email if (validation.email.Count == 0) { // код отсылки email } return Json(validation); } |
Рисунок 2.26 – Проверка Email в RegisterJson Action
Сначало, как видно, идёт проверка возможности отсылки Email. Emails класс использует для проверки регулярное выражание.
public static bool Check(string email) { if (email == null) return false; string reg_email = @"^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])* @([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$"; return new Regex(reg_email).IsMatch(email); } |
Рисунок 2.27 – Проверка Email на правильность составления
Затем проверяется уникальность email, в списке пользователей ищутся с таким же email. Если email верен, проверяется пароль, здесь данная проверка опущена. Далее, если ошибок не найдено, происходит подготовка письма и отсылка. Для создания тела письма используется компонент StringTemplate, позволяющий совмещать списки переменных с шаблоном. Это стандартный подход, среди подобных вспомогательных шаблонных систем.
Try { using (var tran = new TransactionScope()) { Data.User.RegisterUser(name, email, pass); StringTemplateGroup group = new StringTemplateGroup("register", Server.MapPath("~/Views/User")); StringTemplate st = group.GetInstanceOf("RegisterAnonym"); st.SetAttribute("user", Ctx.User); Email("Письмо активации", st.ToString()); tran.Complete(); } FormsAuthentication.SetAuthCookie(email, true); } catch (DataException dexc) { validation.send.Add("Проблема с базой данных"); } catch (Exception exc) { validation.send.Add("Ошибка отсылки активации"); } |
Рисунок 2.28 – Составление Email и отсылка