views:

1584

answers:

4

I've seen a few examples of RSS Feeds in ASP.NET MVC, like this, and some samples in projects (like Oxite), but none of them are complete.

Eg. None of them check for the header

  If-Modified-Since

in the request, to save bandwidth.

I do not want to reinvent the wheel, so I stop here asking for some directions.

+3  A: 

I haven't seen it implement HTTP_IF_MODIFIED_SINCE, but I would look into using the SyndicationFeed class. It makes it very simple to handle feeds without any parsing. I am currently using it for Atom feeds, but it is supposed to work for RSS as well:

function SyndicationFeed GetFeed(string url) {
  XmlReader reader = XmlReader.Create(url);
  SyndicationFeed feed = SyndicationFeed.Load(reader);
  return feed;
}

public ActionResult ShowFeed()
{
  string feedUrl = "somefeedurl";
  SyndicationFeed feed = GetFeed(feedUrl);
  return View(feed);
}

...then in the view:

  <%foreach (var item in ViewData.Model.Items) { %>
    <li><a href="<%=item.Id %>"><%=item.Title.Text %></a></li>
  <% } %>
Trevor de Koekkoek
P.S. The class is in the System.ServiceModel.Web assembly
Trevor de Koekkoek
Your solution is for consuming feeds, not publishing (as asked)
Eduardo Molteni
Getting this error in view.CS1061: 'object' does not contain a definition for 'Items' and no extension method 'Items' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)
coure06
+1  A: 

I ended up with this. Please comment or edit the post if you find any error or better way to do it.

RssController

Imports System.ServiceModel.Syndication
Imports System.Xml
Imports System.Web.HttpContext

Function MasterRSS()
    Dim baseURL As String = "http://www.mysite.com"
    Dim feed As New SyndicationFeed("MySite - Master RSS", "", New Uri(baseURL))
    ''//Add a custom attribute.
    Dim xqName As New XmlQualifiedName("CustomAttribute")
    feed.AttributeExtensions.Add(xqName, "Value")

    Dim sp As New SyndicationPerson("[email protected]", "Jerry Seinfeld", "http://Jerry.blog.com")
    feed.Authors.Add(sp)

    Dim category As New SyndicationCategory("Category")
    feed.Categories.Add(category)

    feed.Contributors.Add(New SyndicationPerson("[email protected]", "Cosmo Kramer", "http://kramer.blog.com"))
    feed.Copyright = New TextSyndicationContent("MySite 2008")
    feed.Description = New TextSyndicationContent("My description")

    ''//Add a custom element.
    Dim doc As New XmlDocument()
    Dim feedElement As XmlElement = doc.CreateElement("CustomElement")
    feedElement.InnerText = "Some text"
    feed.ElementExtensions.Add(feedElement)

    feed.Generator = "Custom"
    feed.Id = "MySiteFeedID"
    feed.ImageUrl = New Uri("http://www.mysite.com/content/images/logo.png")

    ''//Items
    Dim ModifiedSince As Date
    If Not Date.TryParse(Current.Request.Headers("If-Modified-Since"), ModifiedSince) Then
        ModifiedSince = Date.Today.AddDays(-30) ''//Or whatever make sense to you.         
    Else
        ModifiedSince.AddMinutes(-5) ''//Just in case, we do not want to miss any item. 
    End If

    ''//the list of items.
    Dim items As New List(Of SyndicationItem)()


    Dim db As New mainDataContext
    Dim textContent As TextSyndicationContent, Item As SyndicationItem

    ''//Then send the list of post, comments, whatever.
    Dim Posts = (From p In db.Posts Where c.Date >= ModifiedSince Order By p.Date Descending)
    For Each Post In Posts
        Dim sb As New StringBuilder
        sb.AppendFormat("<p>{0}</p>", Post.FullText)
        textContent = New TextSyndicationContent(sb.ToString, TextSyndicationContentKind.Html)
        Item = New SyndicationItem("Post " + Post.PostID.ToString, textContent, New Uri(baseURL + "/Post/View/" + Post.PostID.ToString), "Post" + Post.PostID.ToString, Post.Date)
        items.Add(Item)
    Next


    If items.Count = 0 Then
        ''//send a 304 to the browser.
        Return View("304")
    End If

    feed.Items = items
    feed.Language = "es-ar"
    feed.LastUpdatedTime = (From i In items Select i.LastUpdatedTime Order By LastUpdatedTime Descending).FirstOrDefault

    ''//Not needed in this sample.
    ''//Dim link As New SyndicationLink(New Uri("http://server/link"), "alternate", "Link Title", "text/html", 1000)
    ''//feed.Links.Add(link)

    ViewData("feed") = feed
    Return View("Rss")

End Function

Rss View (rss.aspx)

Dim htmlwriter As System.IO.Stream = Response.OutputStream
Dim rssWriter As System.Xml.XmlWriter = System.Xml.XmlWriter.Create(htmlwriter)
Dim feed As System.ServiceModel.Syndication.SyndicationFeed = ViewData("feed")
Dim rssFormatter As New System.ServiceModel.Syndication.Rss20FeedFormatter(feed)
rssFormatter.WriteTo(rssWriter)
rssWriter.Close()

This way, you can reuse the view

Eduardo Molteni
It seems you're confusing the server variable name with the actual header name: Current.Request.Headers("HTTP_IF_MODIFIED_SINCE") should be Current.Request.Headers("If-Modified-Since")
Duncan Smart
@Duncan: Fixed!
Eduardo Molteni
+2  A: 

If you use HttpContext.Response.Cache.SetCacheability, SetExpires and SetMaxAge then the ASP.NET caching system will take care of this for you - it understands the If-Modified-Since header. This blog entry shows how you can create an MVC Action Filter to do the necessary in MVC.

Duncan Smart
Yes, but I want to go a little further after the cache expires and check if there are new items after If-Modified-Since
Eduardo Molteni
A: 

I have one question. How to generate the rss so as the google reader and other reader can read them without creating our own view or is it necessary to create our own view.

You need a view to show the results. In my solution, you only need one generic view.
Eduardo Molteni