[ Полезный рекламный блок ]
Попробуйте свои силы в игре, где ваши навыки программирования на 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#?
Пройдите наш тест на 13 вопросов, чтобы узнать, как много вы знаете на самом деле!
C# Braincheck |
Вы хотите научится писать код на языке программирования C#?
Создавать различные информационные системы, состоящие из сайтов, мобильных клиентов, десктопных приложений, телеграмм-ботов и т.д.
Переходите к нам на страницу Dijix и ознакомьтесь с условиями обучения, мы специализируемся только на индивидуальных занятиях, как для начинающих, так и для более продвинутых программистов. Вы можете взять как одно занятие для проработки интересующего Вас вопроса, так и несколько, для более плотной работы. Благодаря личному кабинету, каждый студент повысит качество своего обучения, в вашем распоряжении:
- Доступ к пройденному материалу
- Тематические статьи
- Библиотека книг
- Онлайн тестирование
- Общение в закрытых группах
Живи в своем мире, программируй в нашем.