I'm new to the MVC framework and wondering how to pass the RSS data from the controller to a view. I know there is a need to convert to an IEnumerable list of some sort. I have seen some examples of creating an anonymous type but can not figure out how to convert an RSS feed to a generic list and pass it to the view.
I don't want it to be strongly typed either as there will be multiple calls to various RSS feeds.
Any suggestions.
I've been playing around with a way of doing WebParts in MVC which are basically UserControls wrapped in a webPart container. One of my test UserControls is an Rss Feed control. I use the RenderAction HtmlHelper extension in the Futures dll to display it so a controller action is called. I use the SyndicationFeed class to do most of the work
using (XmlReader reader = XmlReader.Create(feed))
{
SyndicationFeed rssData = SyndicationFeed.Load(reader);
return View(rssData);
}
Below is the controller and UserControl code:
The Controller code is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using System.Xml;
using System.ServiceModel.Syndication;
using System.Security;
using System.IO;
namespace MvcWidgets.Controllers
{
public class RssWidgetController : Controller
{
public ActionResult Index(string feed)
{
string errorString = "";
try
{
if (String.IsNullOrEmpty(feed))
{
throw new ArgumentNullException("feed");
}
**using (XmlReader reader = XmlReader.Create(feed))
{
SyndicationFeed rssData = SyndicationFeed.Load(reader);
return View(rssData);
}**
}
catch (ArgumentNullException)
{
errorString = "No url for Rss feed specified.";
}
catch (SecurityException)
{
errorString = "You do not have permission to access the specified Rss feed.";
}
catch (FileNotFoundException)
{
errorString = "The Rss feed was not found.";
}
catch (UriFormatException)
{
errorString = "The Rss feed specified was not a valid URI.";
}
catch (Exception)
{
errorString = "An error occured accessing the RSS feed.";
}
var errorResult = new ContentResult();
errorResult.Content = errorString;
return errorResult;
}
}
}
The UserControl
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="Index.ascx.cs" Inherits="MvcWidgets.Views.RssWidget.Index" %>
<div class="RssFeedTitle"><%= Html.Encode(ViewData.Model.Title.Text) %> <%= Html.Encode(ViewData.Model.LastUpdatedTime.ToString("MMM dd, yyyy hh:mm:ss") )%></div>
<div class='RssContent'>
<% foreach (var item in ViewData.Model.Items)
{
string url = item.Links[0].Uri.OriginalString;
%>
<p><a href='<%= url %>'><b> <%= item.Title.Text%></b></a>
<% if (item.Summary != null)
{%>
<br/> <%= item.Summary.Text %>
<% }
} %> </p>
</div>
with the code behind modified to have a typed Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.ServiceModel.Syndication;
namespace MvcWidgets.Views.RssWidget
{
public partial class Index : System.Web.Mvc.ViewUserControl<SyndicationFeed>
{
}
}
#Matthew - perfect solution - as an alternative to code behind which tends to break the MVC concept, you can use:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<SyndicationFeed>" %>
<%# Import Namespace="System.ServiceModel.Syndication" %>
Using MVC you don't even need to create a view, you can directly return XML to the feed reader using the SyndicationFeed Class.
(Edit) .NET ServiceModel.Syndication - Changing Encoding on RSS Feed this is a better way. (snip from this link instead.)
http://msdn.microsoft.com/en-us/library/system.servicemodel.syndication.syndicationfeed.aspx
public ActionResult RSS(string id)
{
return return File(MyModel.CreateFeed(id), "application/rss+xml; charset=utf-8");
}
In MyModel
CreateFeed(string id)
{
SyndicationFeed feed = new SyndicationFeed( ... as in the MS link above)
.... (as in the MS link)
//(from the SO Link)
var settings = new XmlWriterSettings
{
Encoding = Encoding.UTF8,
NewLineHandling = NewLineHandling.Entitize,
NewLineOnAttributes = true,
Indent = true
};
using (var stream = new MemoryStream())
using (var writer = XmlWriter.Create(stream, settings))
{
feed.SaveAsRss20(writer);
writer.Flush();
return stream.ToArray();
}
}
A rss is a xml file with special format. You may design a dataset with that generic format and read the rss(xml) with ReadXml method and the uri as the path to the file. Then you have got a dataset you can consume from another clases.
Related
I am trying to integrate stripe in my asp.net application. I am using Visual Studio 17 and target .Net framework is 4.6.1
The post i followed exactly
Below is my controller code:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Stripe;
namespace DonationProgram.Controllers
{
public class DonationController : Controller
{
// GET: Donation
public ActionResult Index()
{
var stripePublishKey = ConfigurationManager.AppSettings["pk_test_Ih2IeiHk6PmK19pdh7UPijhr"];
ViewBag.StripePublishKey = stripePublishKey;
return View();
}
public ActionResult Charge(string stripeEmail, string stripeToken)
{
var customers = new StripeCustomerService();
var charges = new StripeChargeService();
var customer = customers.Create(new StripeCustomerCreateOptions
{
Email = stripeEmail,
SourceToken = stripeToken
});
var charge = charges.Create(new StripeChargeCreateOptions
{
Amount = 500,//charge in cents
Description = "Sample Charge",
Currency = "usd",
CustomerId = customer.Id
});
// further application specific code goes here
return View();
}
}
}
But there are errors in new StripeCustomerService(), new StripeChargeService() & new StripeCustomerCreateOptions saying that "the type or namespace could not be found" though i am using Stripe namespace.
It looks like that blog post is incorrect. What you should really be using is Stripe.CustomerCreateOptions or just CustomerCreateOptions. Likewise for other classes error is reporting.
For example. There is no class StripeCustomerCreateOptions in the stripe-dotnet repo, but there is just CustomerCreateOptions defined here.
Update. A bit more digging through the repo reveals that the blog post was correct at some point, but got out of date. There was a commit in August'18 that removed Stripe prefix from all the customer facing API classes.
I am trying to execute an action on a controller without redirecting to the associated view for that action. For a good example of what I am trying to achieve take a look at the music.xbox.com website. When you add a song to a selected playlist from a popup menu - the page just shows a notification without any redirect or refresh. how is this possible?
What I have is the following:
I have a _playlistPopupMenu partial view that renders the list of playlists as follows:
_PlaylistPopupMenu
#model List<OneMusic.Models.GetPlaylists_Result>
#if (Model.Count > 0)
{
<li style="height:2px" class="divider"></li>
foreach (var item in Model)
{
<li style="height:30px">#Html.DisplayFor(p => item.Name)
#Html.ActionLink(item.Name, "AddSong", "Playlist", new { playlistId = #item.PlaylistId, songId = 1 }, "")
</li>
}
}
The PlaylistController AddSong action is as follows:
public PartialViewResult AddSong(int? playlistId, int? songId)
{
if (ModelState.IsValid)
{
db.AddSongToPlaylist(playlistId, songId);
db.SaveChanges();
return PartialView("_AddToPlaylist", "");
}
return PartialView("_AddToPlaylist", "");
}
I am struggling with what to put in the _AddToPlaylist partial view which I think I need to be able to display a notification of some kind (Possiblly using PNotify add in for Bootstrap). MVC wants to always redirect to ../Playlist/AddSong?playlistId=1&songId=1
Any ideas on how to complete this last part of the problem would be great.
If you don't want "full page reloads" then you need to approach the problem slightly differently, using javascript to alter the page dynamically. A library such as JQuery might make manipulating the DOM a little easier.
Display the popup dynamically using javascript.
When the user hits OK/Submit on the popup, post the data back to the server using javascript, and have the controller you are posting to return some HTML.
Append the returned HTML block (partial view) to an existing div containing playlist tracks.
The most difficult part of this is the asynchronous post. Help with updating a div without reloading the whole page can be found in this question.
EDIT - Example
If you have a controller action (accepting POSTs) with the URL myapp.com/PlayList/AddSong/, then you'd set up JQuery to post to this URL. You'd also set up the data property with any form data which you'd like to post, in your case you'd add playistId and songId to the data property.
You'd then use the result of the AJAX query (HTML) and append it to the existing playlist HTML on the page. So assuming that you want to append the partial view's HTML to a div with ID playlistDiv, and assuming that your partial view returns HTML which is valid when appended to the existing playlist, then your javascript will look something like this:
var data = { playlistId: 1, songId: 1 };
$.ajax({
type: "POST",
url: 'http://myapp.com/PlayList/AddSong/',
data: data,
success: function(resultData) {
// take the result data and update the div
$("#playlistDiv").append(resultData.html)
},
dataType: dataType
});
Disclaimer: I can't guarantee that this code will work 100% (unless I write the program myself). There may be differences in the version of JQuery that you use, etc, but with a little tweaking it should achieve the desired result.
using System.Web.Mvc;
using System.Web.Mvc.Html;
public ActionResult Index()
{
HtmlHelper helper = new HtmlHelper(new ViewContext(ControllerContext, new WebFormView(ControllerContext, "Index"), new ViewDataDictionary(), new TempDataDictionary(), new System.IO.StringWriter()), new ViewPage());
helper.RenderAction("Index2");
return View();
}
public ActionResult Index2(/*your arg*/)
{
//your code
return new EmptyResult();
}
in your controller you must add bottom code:
public ActionResult Index(string msg)
{
if (Request.Url.ToString().Contains("yourNewExampleUrlWithOutRedirect.com"))
{
string html = "";
using (System.Net.WebClient client = new System.Net.WebClient())
{
client.Encoding = Encoding.UTF8;
html = client.DownloadString("https://NewExampleUrl.com/first/index?id=1");
}
Response.Write(html);
}
...
}
your view must be empty so you add bottom code
#{
ViewBag.Title = "sample title";
if (Request.Url.ToString().Contains("yourNewExampleUrlWithOutRedirect.com"))
{
Layout = null;
}else
{
Layout ="~/Views/Shared/_Layout.cshtml"
}
}
#if (Request.Url.ToString().Contains("yourNewExampleUrlWithOutRedirect.com")==false)
{
before view like :
<div>hello world</div>
}
I am still learning ASP.NET/MVC and following along a tutorial by Matt Blagden (You Tube) on building a Blog..
After making the Post Controller I am getting an error in a snippet of code that handles adding a Tag to a Post if it is a new Post. My thought is the method AddToPosts should have been generated when I added a new Entity Data Model.
The error is:
blog.Models.BlogModel does not contain a definition for 'AddToPosts' and no extension method 'AddToPosts'. PostsController.cs accepting a first argument of type 'blog.Models.BlogModel' could be found (are you missing a using directive or an assembly reference?)
The portion of code that is giving me the error is:
if (!id.HasValue)
{
model.AddToPosts(post);
}
Here is the entire Post Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using blog.Models;
using System.Data.EntityModel;
using System.Text;
namespace blog.Controllers
{
public class PostsController : Controller
{
// Access Model
private BlogModel model = new BlogModel();
public ActionResult Index()
{
return View();
}
// A way to input sample data into database
[ValidateInput(false)]
public ActionResult Update(int? id, string title, string body, etc.....)
{
//IF not an admin redirect back to index
if (!IsAdmin)
{
return RedirectToAction("Index");
}
// Get Post
Post post = GetPost(id);
// Populate simple properties
post.Title = title;
post.Body = body;
post.DateTime = dateTime;
post.Tags.Clear(); // first clear tag list
//ensure input sequence of tag names is not null
tags = tags ?? string.Empty;
//Split the sequence into a list of tag names
string[] tagNames = tags.Split(new char[] { ' ' }, etc...
//Get or create each tag that was named
foreach (string tagName in tagNames)
{
//Add the tag
post.Tags.Add(GetTag(tagName));
}
if (!id.HasValue)
{
model.AddToPosts(post);
}
model.SaveChanges();
return RedirectToAction("Details", new { id = post.ID });
}
// PROMPTS USER TO INPUT DATA FOR DATABASE
public ActionResult Edit(int? id)
{
Post post = GetPost(id);
//ACCUMULATES LIST OF CURRENT TAGNAMES
StringBuilder tagList = new StringBuilder();
foreach (Tag tag in post.Tags)
{
//Append each tagname
tagList.AppendFormat("{0} ", tag.Name);
}
//Gives the tagList to the view
ViewBag.Tags = tagList.ToString();
return View(post);
}
private Tag GetTag(string tagName)
{ // if tag is set then Get the Tag, if not create a new one (Just like GetPost)
return model.Tags.Where(x => x.Name == tagName).FirstOrDefault() ?? etc....
}
private Post GetPost(int? id)
{
// IF id is set then Get the Post, if not make a new one..
return id.HasValue ? model.Posts.Where(x => x.ID == id).First() : etc.....
// TODO: don't just return true
public bool IsAdmin
/* READS SESSION STATE */
{
get { return true; /*{ return Session["IsAdmin"] != null && etc...
}
}
}
If "AddToPosts" coming from static class, try adding using statement of namespace where the Add post class exists.
I currently have a web form aspx page that calls RegisterClientScriptBlock. This sends down message text that I use for client side validation e.g.
<script type="text/javascript">
//<![CDATA[
var c_errorMessages = {
RequiredField : "* Mandatory field"
};
//]]>
</script>
The values are generated on the server side based on culture and resource files.
I believe you cannot use RegisterClientScriptBlock with MVC.
Any ideas on how I can achieve this with MVC?
The validation that comes with ASP.NET MVC 2 has built-in support for localized validation messages when using client-side validation. This will take care of registering the client scripts.
Having said that, the most common way to do this in ASP.NET MVC would probably be to simply have the controller feed the view with the data required to render the client script. An example of this would be
Action method in controller:
ViewData["MessagesToClient"] = new[] { "Abandon", "All", "Hope" };
return View("MyView");
Some code in MyView.aspx:
<%= RenderPartial("MyPartialView") %>
Some code in MyPartialView.ascx:
<script>
<% foreach (var message in (IEnumerable<string>)ViewData["MessagesToClient"]) { %>
alert("<%= message %>");
<% } %>
</script>
This way, MyView.aspx doesn't necessarily need to know about the data MyPartialView.ascx needs.
Create an extension method for HtmlHelper which takes your model and renders the javascript.
If you are doing validation:
Have a look at Castle Validation attributes. http://castleproject.org.
You can add attributes to your model (class) ...
[ValidateNonEmpty]
public string Name { get; set; }
and your extension method can determine the presence of these and write the appropriate javascript / jquery.
public static string JSValidation<T>(this HtmlHelper<T> html, T model) where T : class {}
And in your view use the helper:
<%= Html.JSValidation(Model) %>
ASP.NET MVC in Action has a brilliant example of this in Chapter 13.
Thanks for answer bzlm. My solution is pretty much the same as yours except I am not using a user control
In my controller I add to viewdata(this would be generated from method):
ViewData["javascriptBlockRequired"] =
"\r\n<script type='text/javascript'>\r\n//<![CDATA[\r\nvar c_merchant = { \r\n\tSomeField: false};\r\nvar c_errorMessages = {\r\n\tRequiredField : '* Required' \r\n};\r\n//]]>\r\n</script>\r\n";
In my view I output the script at the start of the body:
<body>
<%= ViewData["javascriptBlockRequired"] %>
....
<body/>
1- Add Extentions methods class
public static class Extentions
{
public static void AddRegisterClientsSript(this HtmlHelper helper, string key, string script)
{
Dictionary<string, string> scripts;
if (helper.ViewBag.RegisterClientsSript != null)
{
scripts = (Dictionary<string, string>)helper.ViewBag.RegisterClientsSript;
}
else
{
scripts = new Dictionary<string, string>();
helper.ViewBag.RegisterClientsSript = scripts;
}
if (!scripts.ContainsKey(key))
{
scripts.Add(key, script);
}
}
public static MvcHtmlString RegisterClientsSript(this HtmlHelper helper)
{
var outScripts = new StringBuilder();
if (helper.ViewBag.RegisterClientsSript != null)
{
var scripts = (Dictionary<string, string>)helper.ViewBag.RegisterClientsSript;
foreach (string script in scripts.Values)
{
outScripts.AppendLine(script);
}
}
return MvcHtmlString.Create(outScripts.ToString());
}
public static MvcHtmlString Paging(this HtmlHelper helper, string uniqId)
{
helper.AddRegisterClientsSript("jquerypaginatecss", #"<link href=""/Content/Paginate/jquery.paginate.css"" rel=""stylesheet"" />");
helper.AddRegisterClientsSript("jquerypaginatejs", #"<script src=""/Content/Paginate/jquery.paginate.js""></script>");
}
}
2- Add the namespace in page and use it
#using Mirak.Ui.Components
#section scripts{
#Html.RegisterClientsSript()
}
I am trying to create view page at run time means, when the user type some text in textbox at run time then the view page with that name should get created.
May be my code can help you with this
Controller and descendant of ViewPage:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.UI;
using System.Web.UI.WebControls;
using Site.Models;
using System.Text.RegularExpressions;
using System.Web.Mvc.Html;
namespace Site.Controllers
{
public class MPSViewPage : ViewPage
{
// Here master page is being seted
protected override void OnPreInit(EventArgs e)
{
Random rnd = new Random();
int i = rnd.Next(0, 20);
string masterPageName = (i % 2) == 0 ? "Admin.Master" : "Main.Master";
string pathMasterPageFile = "~/Views/Shared/" + masterPageName;
MasterPageFile = pathMasterPageFile;
base.OnPreInit(e);
}
protected override void OnInitComplete(EventArgs e)
{
//List of ContentPlaceHolder's id is being got. See later
MasterPageAnalizer analizer = new MasterPageAnalizer();
IList<string> contentHolders = analizer.GetBodyPlaceholders(Regex.Match(MasterPageFile, "[^/]*$").ToString());
//Add content to ContentPlaceHolder
foreach (string holder in contentHolders)
{
ContentPlaceHolder placeHolder = (ContentPlaceHolder)Master.FindControl(holder);
if (placeHolder != null)
{
Content content = new Content();
placeHolder.Controls.Add(content);
//Set function for render each content
content.SetRenderMethodDelegate(RenderIndexDeletegate);
}
}
base.OnInitComplete(e);
}
protected void RenderIndexDeletegate(HtmlTextWriter w, Control c)
{
//You can use any html helpers for rendering content
w.Write("Render to <b>" + ((Content)c).Parent.ID +
"</b> url: " + Request.Params["URL"] +
" with query parameter " + ViewData["parameters"] + " <br />" +
Html.Action("GetHtmlStatic", "HtmlStatic", new{area = "HtmlStatic"}));
}
}
public class IndexController : Controller
{
public ActionResult Index(string parameters)
{
ViewData["parameters"] = parameters;
return View();
}
}
}
Master page analizer:
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text.RegularExpressions;
namespace Site.Models
{
public class MasterPageAnalizer
{
private DirectoryInfo dirInfo = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory + "Views\\Shared\\");
public IList<string> MasterPages{
get
{
IList<String> masterPageNames = new List<string>();
FileInfo[] files = dirInfo.GetFiles("*.Master");
foreach (FileInfo file in files)
{
masterPageNames.Add(file.Name);
}
return masterPageNames;
}
}
public IList<string> GetBodyPlaceholders(string masterPageName)
{
IList<string> placeholders = new List<string>();
string masterPagePath = dirInfo + masterPageName;
if (File.Exists(masterPagePath))
{
string masterPageContent = File.ReadAllText(masterPagePath);
Regex expression = new Regex(#"<asp:ContentPlaceHolder.*?ID=""(?<placeHolderId>\w+)"".*?>");
masterPageContent = Regex.Match(masterPageContent, "<body>(.|\n)*</body>",RegexOptions.Multiline).ToString();
MatchCollection matches = expression.Matches(masterPageContent);
foreach (Match match in matches)
{
placeholders.Add(match.Groups["placeHolderId"].Value);
}
}
return placeholders;
}
}
}
Simple view:
<%# Page Title="" Language="C#" Inherits="Site.Controllers.MPSViewPage" %>
Good Luck.
Everything is possible. But if you are after to create a project like CMS, it's not right approach. You have to store the pages' information (such as title, description and etc.) in a data store. Thus, you have merely a single page.