2014年12月30日 星期二

練習題 - ASP.NET MVC 產生 RSS Feed

其實這個功能在這兩年所帶的 ASP.NET MVC 實戰課程裡的一個部分,主要是用來講述如何製作一個自訂的 ActionResult,而剛好產生 RSS Feed 內容的功能並不算太複雜,所以不會使用到太長的課程時間,再加上在網路所搜尋出來有關 ASP.NET MVC 如何產生 RSS Feed 大多都還是用字串組合的方式,好一點的會用到 XmlWriter 操作。

今天剛好同事詢問這一個功能,所以就把課程裡有關 Rss Feed 的部分給翻出來,藉此瞭解如何自訂 ActionResult 以及如何使用比較好的方式來產生 Rss Feed。

 


其實上課時在講到這一段內容的時候,我都會先問來上課的朋友知不知道 RSS,或者是有沒有使用 RSS 閱讀器去訂閱部落格(不管是我的還是其他高手,或是國外的大神),絕大部分的回答都是不清楚 RSS 是什麼,就算是知道 RSS 是什麼的也沒有使用訂閱的功能,總之現在有真正會去使用 RSS 訂閱的人是少之又少。

RSS 是個好東西呀,將有興趣的且又有提供 RSS 內容的部落格或是網站使用 RSS 閱讀器給訂閱起來,當有新的文章時就會在閱讀器裡看到,有很多新的訊息以及豐富的技術資訊都可以從訂閱 RSS 取得,最盛時期大概是 Google Reader 被關閉前的兩三年,自從 Google Reader 被關閉服務之後,大部分原有的 RSS 訂閱使用者就奔向不同的服務,而我則是轉換使用「Feedly」,繼續藉由 Feedly 的服務接收 RSS 內容然後獲取大量的新知與技術。

image

image

其實現在還是有很多網站有提供 RSS 內容供使用者訂閱,例如 Yahoo 奇摩新聞就有提供,

image

舉凡網站如果有看到以下的圖示,都是有提供 RSS 內容,

image

所謂 RSS 就是「簡易資訊聚合」,是一種訊息來源的格式規範,用以聚合經常發佈更新資料的網站內容,例如部落格文章、新聞、影音等網站的資訊。

 

RSS 所指的就是「Really Simple Syndication」。

而我們並不是要使用字串組合串接的方式或是自己去寫類別然後輸出 XML Writer 的方式來產生 RSS,而是要使用 .NET Framework 所提供的「SyndicationFeed 類別」與「Rss20FeedFormatter」來建立 RSS Feed。

命名空間:System.ServiceModel.Syndication

http://msdn.microsoft.com/zh-tw/library/system.servicemodel.syndication(v=vs.110).aspx

MSDN - SyndicatonFeed 類別

http://msdn.microsoft.com/zh-tw/library/system.servicemodel.syndication.syndicationfeed(v=vs.110).aspx

MSDN - SyndicationItem 類別

http://msdn.microsoft.com/zh-tw/library/system.servicemodel.syndication.syndicationitem(v=vs.110).aspx

MSDN - Rss20FeedFormatter 類別

http://msdn.microsoft.com/zh-tw/library/system.servicemodel.syndication.rss20feedformatter(v=vs.110).aspx

 

實作 RSS Feed

使用 Visual Studio 2013 新建立一個 ASP.NET MVC 網站,然後如同之前的練習題與範例一樣,使用 LocalDB 以及 Norwwind 資料庫,並且建立 ADO.NET 實體資料模型,

image

接著就是將專案加入「System.ServiceModel」組件參考

SNAGHTMLb15f42c

再來就是要建立 RssActionResult 類別,就如同我在前面的文章裡有提過,這一類屬於網站基礎建設的類別,常常會有很多人不知道要往那裡放,如果是以前都是用網站專案開發 ASP.NET WebForm 的朋友就會去建立一個「App_Code」資料夾,然後什麼東西都往裡頭丟,或者是看到 App_ 開頭的資料夾,就不管是有什麼作用就往裡面擺(例如… App_Start),我之前的建議是在 MVC 網站根目錄下新建立一個「Infrastructure」目錄,然後在這個目錄裡在依據類別的不同再建立不同的資料夾,

image

RssActionResult.cs

using System.ServiceModel.Syndication;
using System.Web.Mvc;
using System.Xml;
 
namespace WebApplication1.Infrastructire.ActionResults
{
    public class RssActionResult : ActionResult
    {
        private readonly SyndicationFeed feed;
 
        public RssActionResult()
        {
        }
 
        public RssActionResult(SyndicationFeed feed)
        {
            this.feed = feed;
        }
 
        public override void ExecuteResult(ControllerContext context)
        {
            context.HttpContext.Response.ContentType = "application/rss+xml";
            var formatter = new Rss20FeedFormatter(feed);
            using (var writer = XmlWriter.Create(context.HttpContext.Response.Output))
            {
                formatter.WriteTo(writer);
            }
        }
    }
}

 

接著建立顯示 RSS 內容的 Controller,

FeedController.cs

image

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Syndication;
using System.Web.Mvc;
using WebApplication1.Infrastructire.ActionResults;
using WebApplication1.Models;
 
namespace WebApplication1.Controllers
{
    public class FeedController : Controller
    {
        private readonly Northwind db = new Northwind();
 
        public ActionResult Index()
        {
            var feed = GetFeedData();
            return new RssActionResult(feed);
        }
 
        private SyndicationFeed GetFeedData()
        {
            var hostUrl = string.Format("{0}://{1}",
                Request.Url.Scheme,
                Request.Headers["host"]);
 
            var feed = new SyndicationFeed(
                "mrkt 的程式學習筆記 - 範例",
                "This is a feed from ASP.NET MVC - Rss Feed Sample",
                new Uri(string.Concat(hostUrl, "/Rss/")));
 
            var items = new List<SyndicationItem>();
 
            var products = db.Products.OrderByDescending(x => x.ProductID);
 
            foreach (var p in products)
            {
                var item = new SyndicationItem(
                    string.Concat(p.Category.CategoryName, " - ", p.ProductName),
                    string.Format("Quantity per Unit: {0}, Unit Price: {1}",
                        p.QuantityPerUnit,
                        p.UnitPrice),
                    new Uri(string.Concat(hostUrl, "/Products/Details?id=", p.ProductID)),
                    "ID",
                    DateTime.Now);
 
                items.Add(item);
            }
 
            feed.Items = items;
            return feed;
        }
    }
}

好的,這樣就已經完成了 RSS Feed 的功能與內容提供了,以下就是執行的結果,

這是在 Firefox 瀏覽器顯示的樣子

image

這是在 IE 瀏覽器顯示的樣子

image

 

Atom Feed

還有另外一種聚合格式就是 Atom Feed,在實作時也是一樣使用 System.ServiceModel 命名空間裡的類別就可以完成,原本 RSS Feed 輸出內容是使用「Rss20FeedFormatter」類別,而 Atom Feed 則是使用「Atom10FeedFormatter」類別,

MSDN - Atom10FeedFormatter 類別

http://msdn.microsoft.com/zh-tw/library/system.servicemodel.syndication.atom10feedformatter(v=vs.110).aspx

另外建立 AtomActionResult.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Syndication;
using System.Web;
using System.Web.Mvc;
using System.Xml;
 
namespace WebApplication1.Infrastructire.ActionResults
{
    public class AtomActionResult : ActionResult
    {
        SyndicationFeed feed;
 
        public AtomActionResult() { }
 
        public AtomActionResult(SyndicationFeed feed)
        {
            this.feed = feed;
        }
 
        public override void ExecuteResult(ControllerContext context)
        {
            context.HttpContext.Response.ContentType = "application/atom+xml";
            Atom10FeedFormatter formatter = new Atom10FeedFormatter(this.feed);
            using (XmlWriter writer = XmlWriter.Create(context.HttpContext.Response.Output))
            {
                formatter.WriteTo(writer);
            }
        }
    }
 
}

而在 FeedController 裡只需要增加新的 Action 方法就可以,取得 Feed 內容則可以沿用既有的程式,

image

執行結果

image

 


以上就是這次練習的內容,不需要去傷神又浪費時間去組合字串,也不需要另外去建立裝載 Feed 資料的物件類別,也不用絞盡腦汁去想怎麼以 XmlWriter 去呈現 Feed 資料,就使用 SyndicationFeed 類別以及 System.ServiceModel 命名空間的相關類別,然後建立相對應的 Custom ActionResult,就可以以比較簡易的方式建立 RSS/Atom Feed。

 

以上

1 則留言:

  1. 請問可以開放xml檔案嗎
    我嘗試自己製作app.xml
    app.xml放在自己的伺服器上,可藉由上傳到feedburner,之後就可以在feedly看到(輸入burner給的網址)
    可是為甚麼我直接透過自己的伺服器的網址(http//:ip/app.xml)卻無法顯示在feedly

    回覆刪除

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。