Русский
Русский
English
Статистика
Реклама

Onion

Чесночно-луковый хостинг как поднять веб-ресурс, чтобы не отобрали домен

24.07.2020 12:09:40 | Автор: admin


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

Глобальный DNS прекрасная вещь, пережившая не одно десятилетие. Но у него есть фундаментальная проблема ваш домен могут просто разделегировать, если вдруг решат, что вы что-то нарушили. Ну или у кого-то с деньгами и связями будет на вас зуб. Историю того же torrents.ru все помнят. Если по каким-то причинам вы хотите убрать подобные риски можно посмотреть в сторону оверлейных сетей, у которых просто нет регулятора, способного разделегировать доменное имя. Поэтому будем поднимать onion- и i2p-веб-ресурсы.

Луковые кольца


Начнем с классики. Я думаю, что Хабре почти все использовали Tor в виде бандла Tor-browser. Меня это сильно выручало, когда в процессе охоты за Telegram вдруг начали резко рвать связность с крупнейшими хостерами в самых неожиданных местах. В этом режиме Tor использует классическое луковое шифрование, послойно заворачивая данные таким образом, чтобы было невозможно установить источник и конечную цель пакета. Тем не менее конечной точкой маршрута все равно является обычный интернет, куда мы в итоге попадаем через Exit-ноды.

У этого решения есть несколько проблем:

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

Готовим контент и обычный веб-сервер


Поэтому будем поднимать onion-ресурс непосредственно внутри сети, без выхода в обычный интернет. Например, как дополнительную резервную точку входа на свой ресурс. Предположим, что у вас уже есть веб-сервер с некоторым контентом, который отдает nginx. Для начала, если вы не хотите светиться в общедоступном интернете, не поленитесь зайти в iptables и настроить firewall. У вас должен быть заблокирован доступ в вашему веб-серверу отовсюду, кроме localhost. В результате вы получили сайт, доступный локально по адресу localhost:8080/. Дополнительное прикручивание https тут будет избыточным, так как транспорт tor возьмет на себя эту задачу.

Разворачиваем TOR


Я буду рассматривать установку на примере Ubuntu, но с другими дистрибутивами принципиальных отличий не будет. Для начала определимся с репозиторием. Официальная документация не рекомендует использовать пакеты, которые мейнтейнятся самим дистрибутивом, так как они могут содержать критические уязвимости, уже исправленные разработчиками в апстриме. Более того, разработчики рекомендуют использовать механизм автоматических обновлений unattended-upgrades, для того чтобы гарантировать их своевременную доставку.

Создаем файл для дополнительного репозитория:

# nano /etc/apt/sources.list.d/tor.list

И добавляем в него нужные адреса:

deb https://deb.torproject.org/torproject.org bionic maindeb-src https://deb.torproject.org/torproject.org bionic main

Теперь надо позаботиться о gpg ключе, без которого сервер совершенно разумно не будет доверять новым пакетам.

# curl https://deb.torproject.org/torproject.org A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | gpg --import# gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | apt-key add -

Теперь можно установить основной пакет из апстрима и связку ключей для автоматического обновления подписи.

# apt update# apt install tor deb.torproject.org-keyring

Настраиваем проксирование


В /etc/tor/torrc вы найдете конфигурационный файл демона. После его обновления не забудьте выполнить его рестарт.
Сразу хочу предупредить особо любопытных пользователей. Не включайте relay-режим на домашней машине! Особенно в режиме exit-ноды. Могут постучаться. На VPS я бы тоже не стал конфигурировать ноду как relay, так как это создаст довольно ощутимую нагрузку как на процессор, так и на траффик. На широком канале вы легко выйдете за 2-3 терабайта в месяц.

Найдите в torrc секцию следующего вида:

############### This section is just for location-hidden services ###

Сюда необходимо прописать ваш локалхостный веб-ресурс. Примерно так:

HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/
HiddenServicePort 80 127.0.0.1:8080


Или вы можете использовать unix-сокеты:

HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/
HiddenServicePort 80 unix:/path/to/socket


Получаем адрес


Все, теперь рестартуем демон tor через systemctl и смотрим в HiddenServiceDir. Там будет лежать несколько файлов приватный ключ и ваш луковый hostname. Он представляет собой случайный идентификатор из 16 символов. Например, gjobqjj7wyczbqie.onion адрес поискового ресурса Candle. Адрес полностью случаен, но при достаточно длительном переборе можно сгенерировать человекочитаемую пару из адреса и приватного ключа. Конечно, не на все 16 символов на это ушло бы миллиарды лет. Например, у всем известного каталога книг Флибусты есть зеркало flibustahezeous3.onion, а Facebook потратил кучу ресурсов на то, чтобы выбрать из сгенерированных вариантов наиболее благозвучный: facebookcorewwwi.onion.

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

Чеснок


image
Второй вариант задумывался как еще более параноидальный по своей сути. Проект i2p изначально не задумывался как средство для проксирования трафика в обычный интернет и по архитектуре является полностью закрытой оверлейной сетью. Отдельные гейты в обе стороны существуют, но это скорее исключение. Причем потенциально небезопасное.

image
Красный логотип эталонного i2p и пурпурный i2pd-реализации

У i2p есть несколько вариантов реализации программных узлов-роутеров. Официальная имплементация написана на Java. И она просто чудовищно пожирает все доступные ресурсы как в плане RAM, так и CPU. Тем не менее именно она считается эталонной и проходит регулярный аудит. Я бы порекомендовал вам использовать куда более легковесный вариант i2pd, написанный на C++. У него есть свои нюансы, из-за которых могут не работать некоторые i2p-приложения, но в целом это отличная альтернативная реализация. Проект активно пилится в настоящее время.

Устанавливаем демона


Самое удобное, что авторы предусмотрели множество вариантов развертывания, включая docker и snap. Можно пойти путем классического репозитория.

sudo add-apt-repository ppa:purplei2p/i2pdsudo apt-get updatesudo apt-get install i2pd

Но я бы советовал использовать snap. Он не только быстро и удобно развернет демона, но и обеспечит автоматическое обновление прямо из апстрима, в зависимости от выбранного канала распространения.

no_face@i2pd:~$ snap info i2pdname:      i2pdsummary:   Distributed anonymous networking frameworkpublisher: Darknet Villain (supervillain)store-url: https://snapcraft.io/i2pdlicense:   BSD-3-Clausedescription: |  i2pd (I2P Daemon) is a full-featured C++ implementation of I2P client.  I2P (Invisible Internet Protocol) is a universal anonymous network layer.  All communications over I2P are anonymous and end-to-end encrypted,  participants don't reveal their real IP addresses.snap-id: clap1qoxuw4OdjJHVqEeHEqBBgIvwOTvchannels:  latest/stable:    2.32.1 2020-06-02 (62) 16MB -  latest/candidate:   latest/beta:        latest/edge:      2.32.1 2020-06-02 (62) 16MB -


Установите snap, если вы этого еще не сделали и установите stable вариант по умолчанию:
apt install snapdsnap install i2pd

Конфигурируем


У i2pd в отличие от web-gui Java версии нет такого количества настроек, крутилочек и вкладок. Только самое необходимое до аскетичности. Тем не менее проще всего будет настроить его напрямую в конфигурационном файле.
Для того чтобы ваш веб-ресурс стал доступен в i2p, его необходимо его проксировать аналогично варианту с onion. Для этого зайдите в файл ~/.i2pd/tunnels.conf и добавьте ваш бэкенд.

[anon-website]
type = http
host = 127.0.0.1
port = 8080
keys = anon-website.dat

После рестарта демона вы получите случайный 32-битный адрес. Его можно посмотреть в веб-консоли, которая по умолчанию доступна в 127.0.0.1:7070/?page=i2p_tunnels. Не забудьте разрешить к ней доступ со своего IP-адреса, если необходимо. По умолчанию она доступна только на локальном интерфейсе. Там будет что-то страшноватое вида ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p.

В i2p-сети есть подобие DNS, но он скорее похож на разрозненный список /etc/hosts. Вы подписываетесь в консоли на конкретные источники, которые говорят вам, как добраться до условной flibusta.i2p. Поэтому имеет смысл добавиться с более или менее красивым именем на крупные ресурсы вроде inr.i2p.

Можно ли развернуть i2p и onion у нас?


Сразу хочу предупредить RuVDS не абузоустойчивый хостинг. В случае мотивированной жалобы на нашего клиента мы можем расторгнуть договор и потушить виртуальную машину. Таким же образом поступят большинство хостеров. Врочем, из-за особенностей архитектуры tor и особенно i2p очень сложно, а часто просто невозможно определить где именно хостится веб-сайт.

Тем не менее ничего противозаконного в самом использовании подобных инструментов нет. Поэтому мы не будем возражать, если вы откроете зеркало своего легального веб-ресурса в оверлейных сетях. В любом случае я еще раз настойчиво рекомендую не экспериментировать вслепую с tor на домашней машине. Или IP может попасть в черные списки, или пативен приедет. Лучше арендуйте VPS, это недорого.

Подробнее..

Как я на собеседование готовился 2

15.12.2020 04:09:34 | Автор: admin
Во второй части, я хотел обновить понимание архитектур Onion и n-Tier, а так же DI-frameworks (Autofac и встроенный в net core). Но посмотрев на объем текста, я понял, что n-Tier будет описан очень кратко, за что сразу извиняюсь.
Так же, я постараюсь учесть замечания первой части,



Архитектура Onion


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

(Tg Bot, Phone App, Desktop) => Asp.net Web Api => Database

Создаем проект в Visual studio типа Asp.net Core, где далее выбираем тип проекта Web Api.
Чем он отличается от обычного?
Во-первых, класс контроллера наследуется от класса ControllerBase, который разработан, как базовый для MVC без поддержки возврата представлений(html-кода).
Во-вторых, он предназначен для реализации REST сервисов с охватом всех видов HTTP запросов, а ответом на запросы Вы получаете json с явным указанием статуса ответа. Так же, Вы увидите, что контроллер, который создастся по-умолчанию, будет помечен атрибутом [ApiController], который имеет полезные опции именно для API.

Теперь нужно решить, как же хранить данные. Так как я знаю, что я читаю не более 12 книг в год, то мне будет достаточно csv-файла, который и будет представлять БД.

Поэтому я создаю класс, который описывает книгу:
Book.cs
using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;namespace WebApiTest{    public class Book    {        public int id { get; set; }        public string name { get; set; }        public string author { get; set; }        public int pages { get; set; }        public int readedPages { get; set; }    }}



А затем описываю класс работы с БД:
CsvDB.cs
using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Text;using System.Threading.Tasks;namespace WebApiTest{    public class CsvDB    {        const string dbPath = @"C:\\csv\books.csv";        private List<Book> books;        private void Init()        {            if (books != null)                return;            string[] lines = File.ReadAllLines(dbPath);            books = new List<Book>();            foreach(var line in lines)            {                string[] cells = line.Split(';');                Book newBook = new Book()                {                    id = int.Parse(cells[0]),                    name = cells[1],                    author = cells[2],                    pages = int.Parse(cells[3]),                    readedPages = int.Parse(cells[4])                };                books.Add(newBook);            }        }        public int Add(Book item)        {            Init();            int nextId = books.Max(x => x.id) + 1;            item.id = nextId;            books.Add(item);            return nextId;        }        public void Delete(int id)        {            Init();            Book selectedToDelete = books.Where(x => x.id == id).FirstOrDefault();            if(selectedToDelete != null)            {                books.Remove(selectedToDelete);            }        }        public Book Get(int id)        {            Init();            Book book = books.Where(x => x.id == id).FirstOrDefault();            return book;        }        public IEnumerable<Book> GetList()        {            Init();            return books;        }        public void Save()        {            StringBuilder sb = new StringBuilder();            foreach(var book in books)                sb.Append($"{book.id};{book.name};{book.author};{book.pages};{book.readedPages}");            File.WriteAllText(dbPath, sb.ToString());        }        public bool Update(Book item)        {            var selectedBook = books.Where(x => x.id == item.id).FirstOrDefault();            if(selectedBook != null)            {                selectedBook.name = item.name;                selectedBook.author = item.author;                selectedBook.pages = item.pages;                selectedBook.readedPages = item.readedPages;                return true;            }            return false;        }    }}


Дальше дело за малым, дописать API, чтобы была возможность взаимодействовать с ним:
BookController.cs
using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.Logging;namespace WebApiTest.Controllers{    [ApiController]    [Route("[controller]")]    public class BookController : ControllerBase    {        private CsvDB db;        public BookController()        {            db = new CsvDB();        }        [HttpGet]        public IEnumerable<Book> GetList() => db.GetList();        [HttpGet("{id}")]        public Book Get(int id) => db.Get(id);        [HttpDelete("{id}")]        public void Delete(int id) => db.Delete(id);        [HttpPut]        public bool Put(Book book) => db.Update(book);    }}


А дальше осталось лишь дописать UI, который был бы удобен. И все работает!
Круто же! Но нет, жена попросила, чтобы у неё тоже был доступ к такой удобной штуке.
Какие же нас ждут трудности? Во-первых, теперь нужно для всех книг добавить столбец, который обозначит айди пользователя. Поверьте, это не будет комфортно в случае с csv-файлом. Так же, теперь нужно и самих юзеров добавить! Да и теперь нужна какая-либо логика, чтобы жена не видела, что я дочитываю третий сборник Донцовой, вместо обещанного Толстого.

Давайте попробуем расширить этот проект к нужным требованиям:
Возможность создать аккаунт юзера, у который сможет хранить список своих книг и дополнять, сколько в какой он прочитал.
Честно, я хотел написать пример, но количество вещей, которые не хотелось бы делать, резко убили желание:
Создание контроллера, который отвечал бы за авторизацию и отдачу данных именно юзера;
Создание новой сущности Пользователь, а так же обработчика под неё;
Впихивание логики или в сам контроллер, из-за чего тот бы раздуло, или же в отдельный класс;
Переписывания логики работы с базой данных, ведь теперь или два csv-файла, или же переходить на БД

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

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

Историческая справка
Для решения проблем этих проблем была предложена луковичная архитектура.
Термин луковая архитектура был введен Джеффри Палермо в 2008 году. Эта архитектура обеспечивает лучший способ создания приложений, обеспечивающих лучшую тестируемость, ремонтопригодность и надежность таких инфраструктур, как базы данных и службы. Основная цель этой архитектуры решить проблемы, с которыми сталкивается трехуровневая архитектура или многоуровневая архитектура, и предоставить решение общих проблем, таких как объединение и разделение задач.
Вольный перевод отсюда


Поэтому мы попробуем реализовать наше приложение в Onion-стиле, чтобы показать преимущества данного способа.

Onion-архитектура представляет собой разделение приложения на уровни. При чем есть один независимый уровень, который находится в центре архитектуры.

Onion-архитектура во многом опирается на принцип инверсии зависимостей. Пользовательский интерфейс взаимодействует с бизнес-логикой через интерфейсы.

Принцип инверсии зависимостей
Принцип инверсии зависимостей (Dependency Inversion Principle) служит для создания слабосвязанных сущностей, которые легко тестировать, модифицировать и обновлять. Этот принцип можно сформулировать следующим образом:
Модули верхнего уровня не должны зависеть от модулей нижнего уровня. И те и другие должны зависеть от абстракций.
Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.



Классический проект в таком стиле состоит из четырех слоев:
  • Уровень объектов домена(Core)
  • Уровень репозитория(Repo)
  • Уровень обслуживания(Service)
  • Уровень внешнего интерфейса (веб / модульный тест)(Api)


Все слои направлены к центру(Core). Центр независим.

Уровень объектов домена


Это центральная часть приложения в которой описываются объекты, которые работают с базой данных.

Создадим новый проект в решении, который будет иметь выходной тип Библиотека классов. Я назвал его WebApiTest.Core

Создадим класс BaseEntity, который будет будет иметь общие свойства объектов.
BaseEntity.cs
    public class BaseEntity    {        public int id { get; set; }    }


Офф-топ
В данном случае, я буду использовать только свойство id, но вполне удобно будет сюда вынести всякие служебные вещи, вроде dateAdded, dateModifed и т.п.

Далее, создадим класс Book, который наследуется от BaseEntity
Book.cs
public class Book: BaseEntity
{
public string name { get; set; }
public string author { get; set; }
public int pages { get; set; }
public int readedPages { get; set; }
}


Для нашего приложения этого будет пока-что достаточно, так что переходим к следующему уровню

Уровень репозитория


Теперь перейдем к реализации уровня репозитория. Создаем проект с типом Библиотека классов с названием WebApiTest.Repo
Мы будем использовать внедрение зависимостей, поэтому мы будем передавать параметры через конструктор, чтобы сделать их более гибкими. Таким образом, мы создаем общий интерфейс репозитория для операций с сущностями, чтобы мы могли разработать слабосвязанное приложение. Приведенный ниже фрагмент кода предназначен для интерфейса IRepository.
IRepository.cs
    public interface IRepository <T> where T : BaseEntity    {        IEnumerable<T> GetAll();        int Add(T item);        T Get(int id);        void Update(T item);        void Delete(T item);        void SaveChanges();    }




Теперь давайте реализуем класс репозитория для выполнения операций с базой данных над сущностью, которая реализует IRepository. Этот репозиторий содержит конструктор с параметром pathToBase, поэтому, когда мы создаем экземпляр репозитория, мы передаем путь к файлу, чтобы класс понимал, откуда забирать данные.
CsvRepository.cs
public class CsvRepository<T> : IRepository<T> where T : BaseEntity    {        private List<T> list;        private string dbPath;        private CsvConfiguration cfg = new CsvConfiguration(CultureInfo.InvariantCulture)        {            HasHeaderRecord = false,            Delimiter = ";"        };        public CsvRepository(string pathToBase)        {            dbPath = pathToBase;            using (var reader = new StreamReader(pathToBase)) {                using (var csv = new CsvReader(reader, cfg)) {                    list = csv.GetRecords<T>().ToList(); }            }        }        public int Add(T item)        {            if (item == null)                throw new Exception("Item is null");            var maxId = list.Max(x => x.id);            item.id = maxId + 1;            list.Add(item);            return item.id;        }        public void Delete(T item)        {            if (item == null)                throw new Exception("Item is null");            list.Remove(item);        }        public T Get(int id)        {            return list.SingleOrDefault(x => x.id == id);        }        public IEnumerable<T> GetAll()        {            return list;        }        public void SaveChanges()        {            using (TextWriter writer = new StreamWriter(dbPath, false, System.Text.Encoding.UTF8))            {                using (var csv = new CsvWriter(writer, cfg))                {                    csv.WriteRecords(list);                }            }        }        public void Update(T item)        {            if(item == null)                throw new Exception("Item is null");            var dbItem = list.SingleOrDefault(x => x.id == item.id);            if (dbItem == null)                throw new Exception("Cant find same item");            dbItem = item;        }



Мы разработали сущность и контекст, которые необходимы для работы с базы данных.

Уровень обслуживания


Теперь мы создаем третий уровень луковой архитектуры, который является уровнем обслуживания. Я назвал его WebApiText.Service. Этот уровень взаимодействует как с веб-приложениями, так и с проектами репозиториев.

Мы создаем интерфейс с именем IBookService. Этот интерфейс содержит сигнатуру всех методов, к которым обращается внешний уровень для объекта Book.
IBookService.cs
public interface IBookService    {        IEnumerable<Book> GetBooks();        Book GetBook(int id);        void DeleteBook(Book book);        void UpdateBook(Book book);        void DeleteBook(int id);        int AddBook(Book book);    }


Теперь реализуем его в классе BookService
BookService.cs
public class BookService : IBookService    {        private IRepository<Book> bookRepository;        public BookService(IRepository<Book> bookRepository)        {            this.bookRepository = bookRepository;        }        public int  AddBook(Book book)        {            return bookRepository.Add(book);        }        public void DeleteBook(Book book)        {            bookRepository.Delete(book);        }        public void DeleteBook(int id)        {            var book = bookRepository.Get(id);            bookRepository.Delete(book);        }        public Book GetBook(int id)        {            return bookRepository.Get(id);        }        public IEnumerable<Book> GetBooks()        {            return bookRepository.GetAll();        }        public void UpdateBook(Book book)        {            bookRepository.Update(book);        }    }



Уровень внешнего интерфейса



Теперь мы создаем последний слой луковой архитектуры, который, в нашем случае, внешним интерфейсом, с которым и буду взаимодействовать внешние приложения(бот, десктоп и т.п.). Чтобы создать этот уровень, мы вычищаем наш проект WebApiTest.Api, удаляя класс Book и вычищая BooksController. Этот проект дает возможность для операций с базой данных сущностей, а также контроллер для выполнения этих операций.

Поскольку концепция внедрения зависимостей является центральной для приложения ASP.NET Core, то теперь нам нужно зарегистрировать всё, что мы создали, для использования в приложении.

Внедрение зависимостей


В небольших приложениях на ASP.NET MVC мы относительно легко можем заменить одни классы на другие, вместо одного контекста данных использовать другой. Однако в крупных приложениях это уже будет проблематично сделать, особенно если у нас десятки контроллеров с сотней методов. В этой ситуации нам на помощь может прийти такой механизм как внедрение зависимостей.

И если раньше в ASP.NET 4 и других предыдущих версиях надо было использовать различные внешние IoC-контейнеры для установки зависимостей, такие как Ninject, Autofac, Unity, Windsor Castle, StructureMap, то ASP.NET Core уже имеет встроенный контейнер внедрения зависимостей, который представлен интерфейсом IServiceProvider. А сами зависимости еще называются сервисами, собственно поэтому контейнер можно назвать провайдером сервисов. Этот контейнер отвечает за сопоставление зависимостей с конкретными типами и за внедрение зависимостей в различные объекты.

В самом начале, мы использовали жесткую связь, чтобы использовать CsvDB в контроллере.
private CsvDB db;        public BookController()        {            db = new CsvDB();        }

На первый взгляд ничего плохого здесь нет, но, например, изменилась схема подключения к базе данных: вместо Csv я решил использовать MongoDB или MySql. Кроме того, может потребоваться динамически менять один класс на другой.

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

Используя различные методы внедрения зависимостей, можно управлять жизненным циклом создаваемых сервисов. Сервисы, которые создаются механизмом Depedency Injection, могут представлять один из следующих типов:

  • Transient: при каждом обращении к сервису создается новый объект сервиса. В течение одного запроса может быть несколько обращений к сервису, соответственно при каждом обращении будет создаваться новый объект. Подобная модель жизненного цикла наиболее подходит для легковесных сервисов, которые не хранят данных о состоянии
  • Scoped: для каждого запроса создается свой объект сервиса. То есть если в течение одного запроса есть несколько обращений к одному сервису, то при всех этих обращениях будет использоваться один и тот же объект сервиса.
  • Singleton: объект сервиса создается при первом обращении к нему, все последующие запросы используют один и тот же ранее созданный объект сервиса

Для создания каждого типа сервиса во встроенном контейнере .net core предназначен соответствующий метод AddTransient(), AddScoped() и AddSingleton().

Мы могли бы использовать стандартный контейнер(провайдер сервисов), но он не поддерживает передачу параметров, поэтому мне придётся использовать библиотеку Autofac.

Для этого добавим в проект через NuGet два пакета: Autofac и Autofac.Extensions.DependencyInjection.
Теперь изменяем в файле Startup.cs метод ConfigureServices на:
ConfigureServices
 public IServiceProvider ConfigureServices(IServiceCollection services)        {            services.AddMvc();            var builder = new ContainerBuilder();//Создаем контейнер            builder.RegisterType<CsvRepository<Book>>()//Регистрируем CsvRepository                .As<IRepository<Book>>() //Как реализацию IRepository                .WithParameter("pathToBase", @"C:\csv\books.csv")//С параметром pathToBase                .InstancePerLifetimeScope(); //Scope            builder.RegisterType<BookService>()                .As<IBookService>()                .InstancePerDependency(); //Transient             builder.Populate(services); //             var container = builder.Build();            return new AutofacServiceProvider(container);        }



Таким образом, мы связали все реализации с их интерфейсами.

Вернемся к нашему проекту WebApiTest.Api.
Осталось только изменить BooksController.cs
BooksController.cs
[Route("[controller]")]    [ApiController]    public class BooksController : ControllerBase    {        private IBookService service;        public BooksController(IBookService service)        {            this.service = service;        }        [HttpGet]        public ActionResult<IEnumerable<Book>> Get()        {            return new JsonResult(service.GetBooks());        }        [HttpGet("{id}")]        public ActionResult<Book> Get(int id)        {            return new JsonResult(service.GetBook(id));        }        [HttpPost]        public void Post([FromBody] Book item)        {            service.AddBook(item);        }        [HttpPut("{id}")]        public void Put([FromBody] Book item)        {            service.UpdateBook(item);        }        [HttpDelete("{id}")]        public void Delete(int id)        {            service.DeleteBook(id);        }    }



Жмём F5, ждем открытия браузера, переходим на /books и
[{"name":"Test","author":"Test","pages":100,"readedPages":0,"id":1}]


Итог:


В данном тексте, я хотел обновить все свои знания по архитектурному паттерну Onion, а так же по внедрению зависимостей, обязательно с использованием Autofac.
Цель я считаю выполненной, спасибо, что прочитали ;)

n-Tier
В n-уровневой архитектуре приложение разделяется на логические слои и физические уровни.
Слои это способ распределения ответственности и управления зависимостями. Каждый слой несет определенную ответственность. В более высоком слое могут использоваться службы из более низкого слоя, но не наоборот.
Уровни разделяются физически путем запуска на разных компьютерах. С одного уровня можно отправлять вызовы непосредственно на другой уровень или использовать асинхронный обмен сообщениями (очередь сообщений). Каждый слой можно разместить на отдельном уровне, но это не обязательно. Вы можете разместить несколько слоев на одном уровне. Физическое разделение уровней улучшает масштабируемость и устойчивость, но также приводит к увеличению задержки из-за дополнительных операций сетевого взаимодействия.
Подробнее..
Категории: Net , Di , Onion

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru