Phần 1. Giới thiệu Sitemap.xml ?
Sitemaps (sơ đồ trang web) là một cách thức để người quản trị web thông báo cho bộ máy tìm kiếm về các trang web của họ để từ đó công cụ robots của các bộ máy tìm kiếm có thể chọn lọc, sắp xếp dễ dàng, chính xác và hiệu quả nhất. Ở dạng đơn giản nhất, một sơ đồ trang web là một tập tin XML liệt kê URL của các trang web cùng với siêu dữ liệu bổ sung về mỗi URL (thời gian lần cập nhật cuối cùng, mật độ thay đổi trong trang, độ quan trọng của trang so với các trang khác trong website) để các công cụ tìm kiếm thông minh có thể hiểu hơn về các trang web của bạn.
Dưới đây là ví dụ đơn giản vệ một file sitemap.xml:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://www.example.com/</loc>
<lastmod>2005-01-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
</urlset>
Như bạn có thể thấy mỗi file sitemap.xml chứa một danh sách các node url. Mỗi node url lại chứa 4 node con là loc, lastmod, changefred, priority. Mình giải thích ý nghĩa của 4 node này như sau:
Bạn lưu ý rằng 3 giá trị này chỉ là một dấu hiểu để công cụ tìm kiếm có thể xem xét kỹ hơn về trang web của bạn chứ không đảm bảo rằng công cụ tìm kiếm sẽ xử lý chính xác 100% như bạn đề ra. Tóm lại là việc tạo file sitemap.xml cho website sẽ rất tốt trong việc SEO đấy nhé.
Bạn có thể tạo 1 file sitemap.xml tĩnh rồi upload lên thư mục gốc của website nhưng bạn phải sửa đổi file mỗi khi có thay đổi. Phương pháp này là hợp lý nếu website của bạn chứa ít trang web và nhu cầu thay đổi là ít. Còn nếu website của bạn có rất nhiều trang và mật độ thay đổi nội dung mỗi trang nhiều thì bạn hãy làm theo cách khác đó là viết code để tự động tạo sitemap.xml. Các bạn vào đây và nhập link web mình vào nhé https://www.xml-sitemaps.com/
Tự động tạo 1 file sitemap.xml trong ASP.NET MVC là khá đơn giản nhưng đòi hỏi bạn phải viết code nhiều hơn một chút. Đầu tiên là bạn phải tạo class SitemapNode và enum SitemapFrequency. Mình sẽ tạo luôn trong HomeController:
public class SitemapNode
{
public SitemapFrequency? Frequency { get; set; }
public DateTime? LastModified { get; set; }
public double? Priority { get; set; }
public string Url { get; set; }
}
public enum SitemapFrequency
{
Never,
Yearly,
Monthly,
Weekly,
Daily,
Hourly,
Always
}
Class SitemapNode đại diện cho một node url trong file sitemap.xml còn enum SitemapFrequency đại diện cho node changefreq mà mình đã giải thích ở trên.
Bây giờ bạn cần tạo một phương thức trả về một collection SitemapNode. Ở ví dụ bên dưới mình sẽ thêm 3 trang trong HomeController là Index, About, Contact. Sau đó là danh sách các trang chi tiết sản phẩm bằng cách duyệt foreach toàn bộ sản phẩm và lấy ra ID của mỗi sản phẩm để truyền vào tham số.
public IReadOnlyCollection<SitemapNode> GetSitemapNodes()
{
List<SitemapNode> nodes = new List<SitemapNode>();
nodes.Add(
new SitemapNode()
{
Url = Url.Action("Index", "Home", null, Request.Url.Scheme),
Priority = 1
});
nodes.Add(
new SitemapNode()
{
Url = Url.Action("About", "Home", null, Request.Url.Scheme),
Priority = 0.9
});
nodes.Add(
new SitemapNode()
{
Url = Url.Action("Contact", "Home", null, Request.Url.Scheme),
Priority = 0.9
});
foreach (var product in dsSanPham)
{
nodes.Add(
new SitemapNode()
{
Url = Url.Action("Detail","Product",new { id = product.ID }, Request.Url.Scheme)
Frequency = SitemapFrequency.Weekly,
Priority = 0.8
});
}
return nodes;
}
Bạn chú ý là trong phương thức Url.Action() mình có truyền thêm thuộc tính Request.Url.Scheme để lấy URL tuyệt đối thay vì tương đối (http://example.com/Contact thay vì /Contact) nếu không sẽ sai nhé.
Câu hỏi đặt ra là giả sử bạn phân trang danh sách sản phẩm mỗi trang 10 sản phẩm thì duyệt foreach làm sao để phủ toàn bộ URL. Đây là đoạn code để thực hiện điều đó:
int countPage =(int)Math.Ceiling(dsSanPham.Count() / 10.0);
for(int page = 1; page <= countPage; page++)
{
foreach (var product in dsSanPham)
{
nodes.Add(
new SitemapNode()
{
Url = Url.Action("Detail","Product",new { id = product.ID, page = page }, Request.Url.Scheme)
Frequency = SitemapFrequency.Weekly,
Priority = 0.8
});
}
}
Ngoài ra còn nhiều trường hợp khác mà bạn phải duyệt sao cho phủ đầy đủ các trang như lọc theo phân loại, lọc theo nhóm, sắp xếp, ... Cái này thì trình độ coding của bạn cứng thì chả ngại gì cả ^^
Bây giờ việc phải làm tiếp theo là biến collection SitemapNode thành nội dung file sitemap.XML thông qua phương thức GetSitemapDocument:
public string GetSitemapDocument(IEnumerable<SitemapNode> sitemapNodes)
{
XNamespace xmlns = "http://www.sitemaps.org/schemas/sitemap/0.9";
XElement root = new XElement(xmlns + "urlset");
foreach (SitemapNode sitemapNode in sitemapNodes)
{
XElement urlElement = new XElement(
xmlns + "url",
new XElement(xmlns + "loc", Uri.EscapeUriString(sitemapNode.Url)),
sitemapNode.LastModified == null ? null : new XElement(
xmlns + "lastmod",
sitemapNode.LastModified.Value.ToLocalTime().ToString("yyyy-MM-ddTHH:mm:sszzz")),
sitemapNode.Frequency == null ? null : new XElement(
xmlns + "changefreq",
sitemapNode.Frequency.Value.ToString().ToLowerInvariant()),
sitemapNode.Priority == null ? null : new XElement(
xmlns + "priority",
sitemapNode.Priority.Value.ToString("F1", CultureInfo.InvariantCulture)));
root.Add(urlElement);
}
XDocument document = new XDocument(root);
return document.ToString();
}
Tiếp theo bạn mở file App_Start/RouteConfig.cs và bổ sung lệnh routes.MapMvcAttributeRoutes(); trong phương thức RegisterRoutes để sử dụng attribute Route nhé.
Sau đó tạo một action method sau trong HomeController hoặc controller bất kỳ như sau:
Thay vì dùng file tĩnh thì chúng tao tạo vậy sẽ được file động
`Route("sitemap.xml")`
public ActionResult SitemapXml()
{
var sitemapNodes = GetSitemapNodes();
string xml = GetSitemapDocument(sitemapNodes);
return this.Content(xml,"text/xml", Encoding.UTF8);
}
Lưu ý rằng mặc định trong ASP.NET MVC 5 và các phiên bản trở về sau thì việc cấu hình route với phần mở rộng tập tin .xml là không được phép nên bạn cần thêm dòng dưới đây vào file Web.config:
<configuration>
<system.webServer>
<handlers>
<add name="SitemapXml" path="sitemap.xml" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
</configuration>
Tất cả code c# như sau:
public class SitemapNode
{
public SitemapFrequency? Frequency { get; set; }
public DateTime? LastModified { get; set; }
public double? Priority { get; set; }
public string Url { get; set; }
}
public enum SitemapFrequency
{
Never,
Yearly,
Monthly,
Weekly,
Daily,
Hourly,
Always
}
public string GetSitemapDocument(IEnumerable<SitemapNode> sitemapNodes)
{
XNamespace xmlns = "http://www.sitemaps.org/schemas/sitemap/0.9";
XElement root = new XElement(xmlns + "urlset");
foreach (SitemapNode sitemapNode in sitemapNodes)
{
XElement urlElement = new XElement(
xmlns + "url",
new XElement(xmlns + "loc", Uri.EscapeUriString(sitemapNode.Url)),
sitemapNode.LastModified == null ? null : new XElement(
xmlns + "lastmod",
sitemapNode.LastModified.Value.ToLocalTime().ToString("yyyy-MM-ddTHH:mm:sszzz")),
sitemapNode.Frequency == null ? null : new XElement(
xmlns + "changefreq",
sitemapNode.Frequency.Value.ToString().ToLowerInvariant()),
sitemapNode.Priority == null ? null : new XElement(
xmlns + "priority",
sitemapNode.Priority.Value.ToString("F1", CultureInfo.InvariantCulture)));
root.Add(urlElement);
}
XDocument document = new XDocument(root);
return document.ToString();
}
public IReadOnlyCollection<SitemapNode> GetSitemapNodes()
{
List<SitemapNode> nodes = new List<SitemapNode>();
nodes.Add(
new SitemapNode()
{
Url = Url.Action("Index", "Home", null, Request.Url.Scheme),
Priority = 1
});
nodes.Add(
new SitemapNode()
{
Url = Url.Action("About", "Home", null, Request.Url.Scheme),
Priority = 0.9
});
nodes.Add(
new SitemapNode()
{
Url = Url.Action("Contact", "Home", null, Request.Url.Scheme),
Priority = 0.9
});
foreach (var product in dsSanPham)
{
nodes.Add(
new SitemapNode()
{
Url = Url.Action("Detail","Product",new { id = product.ID }, Request.Url.Scheme)
Frequency = SitemapFrequency.Weekly,
Priority = 0.8
});
}
return nodes;
}
`Route("sitemap.xml")`
public ActionResult SitemapXml()
{
var sitemapNodes = GetSitemapNodes();
string xml = GetSitemapDocument(sitemapNodes);
return this.Content(xml,"text/xml", Encoding.UTF8);
}
Bạn chỉ cần sửa lại nội dung trong phương thức GetSitemapNodes() thôi nhé.
Build project và thử truy cập vào link http://domaincuaban/sitemap.xml để kiểm tra xem có thấy nội dung sitemap.xml không nhé. Bạn cũng lưu ý rằng tập tin sitemap.xml thực ra không tồn tại trong source code web của bạn với cách này đâu nhé và điều này cũng không quan trọng vì công cụ robots không yêu cầu website của bạn phải tồn tại file vật lý sitemap.xml mà nó chỉ biết rằng khi truy cập theo địa chỉ domain/sitemap.xml thì sẽ nhận được nội dung sơ đồ trang web của bạn mà thôi. Ví dụ bạn thử truy cập vào link https://atcsmart.com/sitemap.xml xem nhé.
Chúc bạn thực hành thành công. Mọi thắc mắc về bài viết bạn comment bên dưới nhé.