2013-03-12 3 views
1

Я создаю веб-приложение с использованием ASP.NET MVC 4, с хранилищем данных, предоставляемым базой данных T-SQL через Entity Framework. Я интегрирую ведение журнала аудита, когда я иду, и я хотел бы предоставить приятное человеко-читаемое резюме действия, чтобы я мог представить дружеское представление журналов с четкими утверждениями, такими как «Пользователь Боб вошел в систему», «Пользовательская Алиса» обновлена ​​статья 'Foo'»и т.д.Аудит ведения журнала с использованием ASP.NET MVC 4 ActionFilter

записи аудита в настоящее время состоит из:

  • GUID
  • метка времени
  • идентификатор пользователя
  • категория действия (имя контроллера)
  • действия (действия hod name)
  • IsError (boolean; правда, значит, либо это запись об ошибке, или это действие не было успешно завершено)
  • комок сериализованными деталей

На данный момент мой записи используется пользовательский атрибут, который реализует IActionFIlter; метод OnActionExecuting() регистрирует предпринятое действие (сериализует такие вещи, как URL, параметры и т. д.), а метод OnActionExecuted() возвращается и устанавливает IsError в true, если ошибок нет, и добавляет либо возвращенный результат, либо исключение с сообщением об ошибке и стеком след и т. д. до деталей. Я хочу добавить еще один столбец для описания строк, но я не могу видеть аккуратный способ сделать это.

Самое длинное, что я получил, это передать строку атрибуту, что-то вроде «User $ user logged in», а затем метод журнала сканирует строку для символа $ и заменяет это слово чем-либо из словаря параметров, ключевое значение соответствует этому слову (минус символ $). Это немного ограничено; например, если статьи хранятся по идентификационному номеру, то лучше всего вы можете управлять «Пользователь 18 отредактировал статью 37». Нет никакого реального способа получить имя пользователя или статьи; вы не можете передавать данные экземпляра в атрибут, потому что он испечен во время компиляции, и я действительно не хочу, чтобы мой метод ведения журнала делал всевозможные вызовы базы данных для получения таких данных, не в последнюю очередь потому, что тогда это становится невозможным (или, по крайней мере, настоящую боль), чтобы иметь один общий метод каротажа.

Альтернатива всем этим заключается в том, чтобы иметь статический журнал аудита и вызвать что-то вроде AuditRecord.WriteLog(foo); повсюду, возможно, с каким-то классом дескриптора, который я могу использовать (или наследовать) для описания различных типов действий, хранения все параметры и генерация строки описания по мере необходимости, но кажется менее элегантной для меня; Мне очень нравится иметь возможность просто пометить [AuditLog] поверх метода и знать, что он будет записан.

Я хотел бы избежать огромного количества условной логики, например, использовать контроллер и имена действий в каком-либо большом операторе switch, чтобы выбрать правильный шаблон строки. Если бы я мог просто получить такие вещи, как заголовки статей в методе ведения журнала, тогда все будет хорошо. Есть ли простой, простой способ сделать это?

ответ

1

Недавно мы провели аналогичную дискуссию в отношении истории ведения журнала регистрации и применения более сложных правил безопасности в нашем новом проекте MVC.

В конечном итоге самым «изящным» решением, с которым мы столкнулись, было вызов метода в рамках действий контроллера (ваш альтернативный метод).

Например:

[HttpPost] 
public ActionResult CreateItem(Item item) 
{ 
    //Simplified 
    CheckSecurity(SecurityTypes.ItemCreation); 
    LogActivity("Created an item"); 

    //Rest of action code 

} 

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

+0

Я, вероятно, в конечном итоге воспользуюсь этим. Вообще-то, у меня есть некоторый код для него из-за того, что я доволен; существует один метод «LogEvent()», который принимает объект дескриптора, но этот дескриптор имеет некоторый фанковый динамический материал, который позволяет содержать подробные строки описания без особого дублированного кода. Мне просто понравилась идея извлечения кода регистрации; разделение проблем и все такое. Я мог бы разбить его на ведение журнала отладки и ведение журнала аудита и использовать подход «ActionFilter» для материалов отладки, которые будут прочитаны разработчиками, которые будут в основном нуждаться в деталях, а не в дружеском сообщении. – anaximander

+0

Мы были тем же самым из-за того, что хотели его извлечь, но имели те же проблемы, что и вы, просто отказавшись от использования методов в каждом действии! Учитывая, что мы тоже были в крайнем случае ... :) – Oliver

0

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

Как Анаксимандр отметили это выше, основная проблема заключается в том, что атрибуты разрешаются CLR, поэтому срок их службы не может контролировать, и они не смешиваются очень хорошо с контейнером IoC (чтобы они преходящи, по запросу экземпляр и т. д.).

Обычно в .NET новый экземпляр атрибута создается каждый раз, когда он разрешен путем отражения (GetCustomAttribute method).

Кроме того, в случае MVC/webapi атрибуты фильтра действий кэшируются, поэтому они обычно создаются только один раз.

Вывод состоит в том, что атрибуты предназначены только для аннотирования, другими словами, они должны содержать только метаданные (они DTO). К сожалению, я понимаю, что MVC и рамки WebApi не разработаны таким образом. Чтобы ограничить атрибуты фильтра действий простыми DTO и иметь возможность управлять жизненным циклом логической части вокруг них, необходимо принять специальные меры.

Я думаю, что ваш случай использования идеально подходит для решения, предусмотренного в статье great article от Steven van Deursen. Он демонстрирует, как отделять данные атрибутов от логики и основан на фильтрах действий, зарегистрированных глобально, так называемом диспетчере, с контейнером ioc в качестве зависимости. Контейнер не разрешен статически. Он предоставляется в конструкторе глобального фильтра, когда он зарегистрирован при инициализации приложения. Итак, каждый раз, когда он выполняется, он ищет маркер атрибута для выполняемого действия и разрешает общий интерфейс, где атрибут является общим параметром. Вместо того, чтобы иметь атрибут фильтра действий, который объединяет данные и поведение, вы в конечном итоге используете два класса: простой старый атрибут - маркер - и соответствующую реализацию универсального интерфейса для его логической копии. Контейнер используется для разрешения общего интерфейса. Если ваш фильтр зависит от компонентов запроса, вы можете создать реализацию универсального интерфейса с необходимыми сервисами. Если это не зависит от других сервисов, но вам нужно время жизни каждого запроса (например, для измерения времени между началом и концом действия), он также выполняет эту работу, благодаря использованию контейнера для разрешения общего интерфейс. В вышеупомянутой статье содержатся примеры кода для WebApi, MVC и ASP.NET. 5

Также Марк Семанн сделал an article на том же подходе.

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

0

Лучшим способом было бы форматировать эти данные при его просмотре, а не строить эти вещи во время процесса ведения журнала.

Если действие является «логином», и записанный пользователь доступен (что вам нужно сделать), вы создадите это сообщение в средстве просмотра.

Итак, вы регистрируете все необработанные события, а затем создаете «модель представления» или «прочитайте модель» на основе этих данных, которые более наглядны. это может позволить вам повторно проанализировать все необработанные данные, если вы хотите изменить его описание. Вы можете записать много данных, которые еще не используются, поэтому вы можете реализовать его позже в описании.

IMO, методы посыпания таким образом внутри действий не кажутся хорошей идеей, и фильтр действий на контроллере или базовом контроллере является более чистым. если вы хотите сделать это, вы можете использовать структуру AOP (аспектно-ориентированное программирование), чтобы избежать перекрестной резки ...

Смежные вопросы