[ Полезный рекламный блок ]
Попробуйте свои силы в игре, где ваши навыки программирования на C# станут решающим фактором. Переходите по ссылке 🔰.
Розглянемо механізм створення карти сайту. Навряд чи тут можна запропонувати якесь універсальне рішення, все залежить безпосередньо від веб-додатка, від того, як у ньому влаштовано систему маршрутів і посилань, але в принципі можна певною мірою автоматизувати процес створення файлу sitemap.xml.
Для прикладу, створимо простий проект ASP.NET Core Web Application (Веб-додаток ASP.NET Core), який буде отримувати товари з бази даних і міститиме 4 основні дії. Я очікую від вас мінімальні знання концепцій Entity Framework Core, тому в деяких місцях, вдаватися в подробиці не стану.
Генерація sitemap.xml
Конфігурування постачальника бази даних і класу контексту
Добавление контроллера и представления
Створення класу генератора карти
Використання власних маршрутів
Виклад матеріалу в статті буде вестися прискореними темпами, оскільки більша частина роботи стосується генерації sitemap.xml з використанням ASP.NET Core МVС поверх фундаменту, сформованого за допомогою інфраструктури Entity Fгamework Core.
Конфігурування підключення
Для початку завантажимо бібліотеку Entity Framework Core Sql Server:
1 |
Install-Package Microsoft.EntityFrameworkCore.SqlServer |
Перейдемо у файл appsettings.json і пропишемо конфігурацію підключення та логування:
1 2 3 4 5 6 7 8 9 10 11 12 |
{ "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\mssqllocaldb;Database=productStore;Trusted_Connection=True;" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Information" } }, "AllowedHosts": "*" } |
У забезпеченні доступу до даних у БД інфраструктура Entity Framework Core покладається на клас контексту БД. Щоб забезпечити приклад додатка контекстом, додайте в папку Data файл класу на ім’я ApplicationContext.cs з таким кодом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class ApplicationContext : DbContext { public ApplicationContext(DbContextOptions<ApplicationContext> context) : base(context) { Database.EnsureDeleted(); Database.EnsureCreated(); } public DbSet<Product> Products { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Product>().HasData( new Product { Id = Guid.NewGuid(), Name = "Apple", Category = "Fruits"}, new Product { Id = Guid.NewGuid(), Name = "Orange", Category = "Fruits" }, new Product { Id = Guid.NewGuid(), Name = "Cherry", Category = "Fruits" } ); } } |
Модель і Репозиторій
Модель для додатка GameStore буде заснована на списку товарів. Створіть папку Models і додайте в неї файл класу на ім’я Product.cs з таким вмістом:
1 2 3 4 5 6 7 |
public class Product { public Guid Id { get; set; } public string Name { get; set; } public string Category { get; set; } public decimal Price { get; set; } } |
Мені подобається забезпечувати узгоджений доступ до даних у застосунку з використанням патерну “Сховище” (Repository), у якому інтерфейс визначає властивості та методи, призначені для доступу до даних, а для роботи з механізмом зберігання даних застосовується клас реалізації. Перевага використання патерну “Сховище” пов’язана з полегшенням модульного тестування частини MVC додатка, а також із тим, що деталі, які стосуються зберігання даних, приховані від інших частин додатка.
Щоб створити інтерфейс сховища, створіть папку Interfaces і додайте в неї файл інтерфейсу на ім’я IProduct.cs із таким вмістом:
1 2 3 4 5 6 |
public interface IProduct { Product GetProduct(string productId); IEnumerable<string> GetProductsId(); void AddProduct(Product product); } |
Створіть папку Repository і додайте в неї файл класу на ім’я ProductRepository.cs з таким вмістом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class ProductRepository : IProduct { private readonly ApplicationContext _context; public ProductRepository(ApplicationContext context) { _context = context; } public void AddProduct(Product product) { _context.Products.Add(product); _context.SaveChanges(); } public Product GetProduct(string productId) { return _context.Products.FirstOrDefault(e => e.Id.ToString() == productId); } public IEnumerable<string> GetProductsId() { return _context.Products.Select(e=>e.Id.ToString()); } } |
У додатку ASP.NET Core MVC, доступ до об’єктів контексту даних управляється з використанням впровадження залежностей, і тому до класу ProductRepository було додано конструктор, який приймає об’єкт ApplicationContext, який буде надано засобом впровадження залежностей під час виконання.
Конфігурування постачальника бази даних і класу контексту
Додайте в клас Program.cs оператори конфігурації, щоб повідомити інфраструктуру Entity Framework Core про те, яким чином використовувати рядок підключення, який має застосовувати постачальник БД, і як керувати класом контексту:
1 2 3 4 5 6 7 |
builder.Services.AddTransient<IProduct, ProductRepository>(); IConfigurationRoot _confString = new ConfigurationBuilder(). SetBasePath(AppDomain.CurrentDomain.BaseDirectory).AddJsonFile("appsettings.json").Build(); builder.Services.AddDbContext<ApplicationContext>(options => options.UseSqlServer(_confString.GetConnectionString("DefaultConnection"))); |
Для простоти прикладу, створення бази даних відбуватиметься без міграцій. Про створення і наповнення бази даних подбає клас контексту ApplicationContext.
Додавання контролера та подання
Змінимо контролер HomeController, додамо такий вміст:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public class HomeController : Controller { private readonly IProduct _products; public HomeController(IProduct products) { _products = products; } [HttpGet] public ActionResult Index() { return View(); } [HttpGet] public ActionResult Product(string productId) { var product = _products.GetProduct(productId); if (product is not null) { return Content($"{product.Name}"); } return NotFound(); } [HttpGet] public ActionResult About() { return View(); } [HttpGet] public ActionResult Contact() { return View(); } } |
Тут у нас чотири методи, крім того, варто зазначити, що метод Product отримує елементи з бд за ID. Таким чином, у базі даних ще можуть зберігатися безліч елементів, для кожного з яких має формуватися свій шлях типу:
https://localhost:7062/home/product?productId=fe438b5a-dad7-4999-8bc7-2d3d7181ac8a
Створення класу генератора карти
Тепер додамо в проєкт новий клас MapGenerator, який генеруватиме файл sitemap.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
public static class UrlHelperExtensions { public static string AbsoluteRouteUrl(this IUrlHelper urlHelper, string routeName, object routeValues = null) { string scheme = urlHelper.ActionContext.HttpContext.Request.Scheme; return urlHelper.RouteUrl(routeName, routeValues, scheme); } } public class MapGenerator { private readonly IProduct _products; public MapGenerator(IProduct products) { _products = products; } public IReadOnlyCollection<string> GetSitemapNodes(IUrlHelper urlHelper) { string scheme = urlHelper.ActionContext.HttpContext.Request.Scheme; List<string> nodes = new List<string>(); nodes.Add(urlHelper.AbsoluteRouteUrl("Default", new { controller = "Home", action = "Index" })); nodes.Add(urlHelper.AbsoluteRouteUrl("Default", new { controller = "Home", action = "About" })); nodes.Add(urlHelper.AbsoluteRouteUrl("Default", new { controller = "Home", action = "Contact" })); foreach (string productId in _products.GetProductsId()) { nodes.Add(urlHelper.AbsoluteRouteUrl("Default", new { controller = "Home", action = "Product", id = productId })); } return nodes; } public string GetSitemapDocument(IEnumerable<string> sitemapNodes) { XNamespace xmlns = "http://www.sitemaps.org/schemas/sitemap/0.9"; XElement root = new XElement(xmlns + "urlset"); foreach (string sitemapNode in sitemapNodes) { XElement urlElement = new XElement( xmlns + "url", new XElement(xmlns + "loc", Uri.EscapeUriString(sitemapNode))); root.Add(urlElement); } XDocument document = new XDocument(root); return document.ToString(); } } |
Для спрощення створення посилання за маршрутом і його параметрами тут визначено метод розширення AbsoluteRouteUrl, який приймає назву маршруту і його параметри.
Метод GetSitemapNodes() класу MapGenerator генерує всі посилання за методами контролера. Для отримання всіх id товарів із бази даних застосовується допоміжний сервіс, який дістає їх із таблиці Products.
У класі Program.cs додамо сервіс:
1 |
builder.Services.AddTransient<MapGenerator>(); |
У HomeController, отримаємо його:
1 2 3 4 5 6 7 8 |
private readonly IProduct _products; private readonly MapGenerator _generator; public HomeController(IProduct products, MapGenerator generator) { _products = products; _generator = generator; } |
Тепер додамо в HomeController метод, який віддаватиме згенеровану карту сайту:
1 2 3 4 5 6 7 8 9 10 |
[Route("/sitemap.xml")] public ActionResult SitemapXml() { var result = _generator.GetSitemapNodes(this.Url); string host = Request.Scheme + "://" + Request.Host; var sitemapNodes = _generator.GetSitemapNodes(this.Url); string xml = _generator.GetSitemapDocument(sitemapNodes); return this.Content(xml, "text/xml", Encoding.UTF8); } |
І тепер при зверненні шляхом https://localhost:XXXX/sitemap.xml ми отримаємо згенеровану карту:
Использование собственных маршрутов
Фреймворк MVC дає змогу використовувати в додатку маршрутизацію на основі атрибутів. Ми можемо вказати власні маршрути для кожної дії нашого контролера, як ми зробили до цього для дії SitemapXml().
Якщо поглянути на карту сайту, поточний шлях отримання продукту виглядає не дуже, давайте визначимо власний маршрут для отримання товару:
1 2 3 4 5 6 7 8 9 10 11 |
[HttpGet] [Route("product")] public ActionResult Product(string productId) { var product = _products.GetProduct(productId); if (product is not null) { return Content($"{product.Name}"); } return NotFound(); } |
Тепер, крім стандартного маршруту, необхідно визначити свій, для цього в класі Program.cs додамо такий рядок:
1 2 3 |
app.MapControllerRoute( name: "myroute", pattern: "{route}/{id?}"); |
У класі MapGenerator, змінимо реалізацію методу GetSitemapNodes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public IReadOnlyCollection<string> GetSitemapNodes(IUrlHelper urlHelper) { string scheme = urlHelper.ActionContext.HttpContext.Request.Scheme; List<string> nodes = new List<string>(); nodes.Add(urlHelper.AbsoluteRouteUrl("Default", new { controller = "Home", action = "Index" })); nodes.Add(urlHelper.AbsoluteRouteUrl("Default", new { controller = "Home", action = "About" })); nodes.Add(urlHelper.AbsoluteRouteUrl("Default", new { controller = "Home", action = "Contact" })); foreach (string productId in _products.GetProductsId()) { nodes.Add(urlHelper.AbsoluteRouteUrl("myroute", new { route = "product", id = productId })); } return nodes; } |
Перейдемо шляхом https://localhost:XXXX/sitemap.xml і отримаємо згенеровану карту з новими маршрутами:
На цьому стаття “Створення Sitemap.xml для веб додатка Asp.net Core 7”, підійшла до кінця, сподіваюся вам було цікаво. Ви можете завантажити вихідний код у моєму репозиторії — Github.
Поділіться вашим досвідом у коментарях, як ви генеруєте sitemap.xml в Asp.Net Core або інших типах проектів?
Так само вам може бути цікава попередня стаття:
Ви хочете навчитися писати код мовою програмування C#?
Створювати різні інформаційні системи, що складаються з сайтів, мобільних клієнтів, десктопних додатків, телеграм-ботів тощо.
Переходьте до нас на сторінку Dijix і ознайомтеся з умовами навчання, ми спеціалізуємося тільки на індивідуальних заняттях, як для початківців, так і для просунутих програмістів. Ви можете взяти як одне заняття для опрацювання питання, що вас цікавить, так і кілька, для більш щільної роботи. Завдяки особистому кабінету, кожен студент підвищить якість свого навчання, у вашому розпорядженні:
- Доступ до пройденого матеріалу
- Тематичні статті
- Бібліотека книг
- Онлайн тестування
- Спілкування в закритих групах