redmanmale

if-goto

Admin-ka: ASP.Net Core GUI

[ csharp | rest | asp.net-core | gui ]

Abstract

Очень простая и лёгкая админка на ASP.Net Core.
Выглядит как-то так: admin-ka

Introduction

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

Мне же хотелось окинуть одним взглядом всю систему и оценить текущий статус, нагрузку, возможные проблемы. А также я давно хотел пощупать ASP.Net Core.

Methods

Вообще я не очень люблю очень не люблю писать интерфейсы. Но иногда приходится. Так родилась Admin-ka.

Backend

Все модули построены на одной архитектуре, потому имеют обобщённый механизм сбора метрик, которые пишутся в лог. Добавляем новый endpoint, который по запросу отдаёт тоже самое, только в виде json.

[ServiceContract]
public interface IStatService
{
    [OperationContract]
    [WebInvoke(Method = "GET", UriTemplate = "stats", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
    IReadOnlyDictionary<string, StatContract> GetStats();
}

[DataContract]
public class StatContract
{
    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public long SpeedIns { get; set; }

    [DataMember]
    public long SpeedAvg { get; set; }

    [DataMember]
    public long CountAll { get; set; }

    [DataMember]
    public long CountInQ { get; set; }

    [DataMember]
    public bool IsSpeed { get; set; }
}

что превращается в такой json (пример того, что отдаёт сервис)

{
	"QueueIn": {
		"Name": "Receiving",
		"SpeedIns": 120,
		"SpeedAvg": 10874,
		"CountAll": 0,
		"CountInQ": 0,
		"IsSpeed": true
	},
	"QueueProc 1": {
		"Name": "Processing 1",
		"SpeedIns": 0,
		"SpeedAvg": 0,
		"CountAll": 3434,
		"CountInQ": 0,
		"IsSpeed": false
	},
	"QueueProc 2": {
		"Name": "Processing 2",
		"SpeedIns": 0,
		"SpeedAvg": 0,
		"CountAll": 3434,
		"CountInQ": 0,
		"IsSpeed": false
	},
	"QueueProc 3": {
		"Name": "Processing 3",
		"SpeedIns": 0,
		"SpeedAvg": 0,
		"CountAll": 3434,
		"CountInQ": 0,
		"IsSpeed": false
	},
	"QueueOut": {
		"Name": "Sending",
		"SpeedIns": 0,
		"SpeedAvg": 0,
		"CountAll": 3430,
		"CountInQ": 0,
		"IsSpeed": false
	}
}

Mock

Поскольку я хотел в первую очередь показать интерфейс, конкретный бэкенд особой роли не играет, поэтому в качестве заглушечного демо я наваял простейший веб-сервис на Node.js, который просто отдаёт эти json-файлы с диска.

Frontend

С другой стороны мы имеем ASP.Net Core приложение.

В конфиге задаём словарь адресов сервисов, которые будем опрашивать, с названием модуля в качестве ключа. Там же задаются пороговые значения метрик — это очень важный момент — при превышении (или наоборот) соответствующие им ячейки интерфейса будут выделены.

При обращении в корень сервиса (указан в hosting.json), контроллер пинает сервис, который собирает данные со всех сервисов из конфига, и рисуется красивая (или не очень) табличка с метриками.

Tips & Tricks #1. Автообновление

Из интересного. Я хотел сделать автообновление данных, думал как правильно это реализовать. Думал про поллинг (server-side и client-side), про серверные пуши, про SignalR. Хотел сделать максимально быстро и просто, но правильно.

В итоге всё решилось одной строчкой в контроллере:

Response.Headers.Add("Refresh", _config.RefreshInterval.TotalSeconds.ToString(CultureInfo.InvariantCulture));

И всё! По сути это встроенное обновление по таймеру.

Tips & Tricks #2. Standalone

Потом я захотел собрать сервис как standalone aka self-contained aka portable aka .EXE, чтобы запускать на машине без IIS’а. Мда.

По идее это должно делаться командой dotnet publish, однако чтобы это сработало мне пришлось помучиться. Я просмотрел кучу статей, FAQ’ов и постов, MS и SO, кучу всего. Куча мануалов, гайдов и всякого материала, но все приведённые примеры файлов проекта приводили к одному из двух исходов: либо проект нормально открывался в студии, но не работал publish, либо наоборот, работал publish, но при открытии в студии, она не видела зависимостей (стандартной библиотеки), т.е. проект билдился, но не работала подсветка, интеллисенс, ничего. Так жить нельзя.

В интернетах описаны кучи разных конфигураций, все нерабочие, под разные таргеты, разных версий Core с разными настройками. И такое ощущение, что никто реально не понимает, как оно устроено. Сплошное шаманство. А в VS 2017RC вообще снова csproj вместо json файл проекта, и все dotnet-команды не работают.

В итоге я заставил это работать. Я проверил на нескольких доступных мне машинах, что всё работает на текущих версиях, а это VS2015, .Net Core 1.1, .Net Command Line Tools 1.0.0-preview2-1-003177.
Я не уверен, что при следующем обновлении Core SDK, студии или тулинга опять всё не сломается. Точнее, я уверен в обратном. Но пока работает и это хорошо.

Results

На выходе мы имеем самодостаточное, гибко настраиваемое приложение, с помощью которого можно быстро оценить статус системы.

Благодаря REST’у в основе, оно не зависит от бэкенда, благодаря Core — от платформы. В случае необходимости можно быстро подправить формат данных или отображение или ещё что.

Плюс я немного полюбился познакомился с Core и фронтендом (node, npm, bower, bootstrap). Доволен, как слон.

Проект на гитхабе.