您的位置:首页 > 编程语言 > ASP

使用Markdown和ASP.NET MVC3创建基于文本的博客

2012-04-26 12:17 288 查看
前言:

步骤1:创建初始的Blog应用

本次示例使用Phil Haack's Really Empty MVC Project Template,添加文件夹:App_Data/BlogPosts,Content/BlogPostImages;添加Controller: HomeController;以及相关联的View Views/Home/Index.cshtml;和其他Shared Views,初始的CSS样式。完整的Solution结构和完成后的应用运行截图如下:

placeholder:截图1

步骤2:安装Markdown

Markdown是一个text-to-HTML转化工具,针对人群为网络写手,博主等。Markdown允许你用容易读/写的纯文本格式写文章,然后转变成结构正确的XHTML(或者HTML)。Markdown格式的文本设计目的是允许内容以as-is发布,像纯文本,不需要用tag标记等。Markdown是免费软件,遵循BSD-style开源协议license。

既然我们使用MVC 3(语言为C#),可以利用MarkdownSharp,也是一个开源的Markdown处理器的C#实现,在Stack Overflow上被featured。使用Nuget Package Manager console安装(添加一个单独文件MarkdownSharp.dll到Bin目录):

PM> Install-Package MarkdownSharp

步骤3:Blog Post和Summary Data命名约定

Blog post是.txt文件,存储在文件夹AppData/BlogPosts。本次示例,我们对文件blog post和文件blog summary使用如下的命名约定,使用字符分割日期和名称信息:

YYYY-MM-DD[blog-post-name-separated-by-hyphens].txt // blog post markdown syntax content
YYYY-MM-DD[blog-post-name-separated-by-hyphens]_summary.txt // JSON formatted blog summary info
使用两个文件的目的是将Markdown的内容和Blog Post summary。Markdown的目标就是内容的as-is,无须任何额外的标记tag或者指令。YYYY-MM-DD用来表示博客发布时间,[]内为博客标题,使用-字符分开单词。Summary文件使用JSON语法现实blog summary。

步骤4:创建File System Data Model

首先,创建model Models/BlogListing.cs,用来表征创建博客需要的数据。

namespace TxtBasedBlog.Sample.Models
{
public class BlogListing
{
public string Url { get; set; }
public string Title { get; set; }
public string ShortDescription { get; set; }
public string Content { get; set; }
public string Author { get; set; }
public DateTime PostDate { get; set; }
public string Image { get; set; }
}
}
然后,创建model Models/BlogPost.cs负责加载博客文章内容。

namespace TxtBasedBlog.Sample.Models
{
public class BlogPost : BlogListing
{
public string Body { get; set; }
}
}
步骤5:写一个Blog Post示例

遵循上述命名约定,我们创建两个示例文件。Summary文件使用JSON语法,完整的blog post使用Markdown语法

{
Title: "ASP.NET MVC Overview",
Url: "asp_net_mvc_overview",
PostDate: "2012-02-09",
Author: "Microsoft ASP.NET Team",
ShortDescription: "ASP.NET MVC gives you a powerful, patterns-based way to build dynamic websites that enables a clean separation of concerns and that gives you full control over markup for enjoyable, agile development.",
Image: "content/blogpostimages/image001.jpg"
}
**ASP.NET MVC** gives you a powerful, patterns-based way to build dynamic websites that enables a clean separation of concerns and that gives you full control over markup for enjoyable, agile development.

MVC includes many features that enable:

* fast, TDD-friendly development for creating sophisticated applications
* use the latest web standards.

[Learn More About MVC](http://www.asp.net/mvc "Learn more about MVC today!")
步骤6:显示博客列表

在web.config中的appSetting键值中添加BlogPostsDirectory配置,这样可以无需编译修改数据目录。

<appSettings>
...
<add key="BlogPostsDirectory" value="~/App_Data/BlogPosts"/>
...
</appSettings>


Models/BlogFileSystemManager.cs用来检查上面配置的数据目录,然后返回博客列表。

namespace TxtBasedBlog.Sample.Models
{
public class BlogFileSystemManager
{
private string filePathToBlogPosts;

public BlogFileSystemManager(string dirPath)
{
filePathToBlogPosts = dirPath;
}

public List<BlogListing> GetBlogListings(int limit)
{
var allFileNames = getBlogPostsFiles();
var blogListings = new List<BlogListing>();
foreach (var fileName in allFileNames.OrderByDescending(i => i).Take(limit))
{
var fileData = File.ReadAllText(fileName);
var blogListing = new JavaScriptSerializer().Deserialize<BlogListing>(fileData);
blogListings.Add(blogListing);
}
return blogListings;
}

private IEnumerable<string> getBlogPostsFiles()
{
return Directory.GetFiles(filePathToBlogPosts, "*summary.txt").ToList();
}
}
}


HomeController
加载
BlogListing
,在View中渲染。

namespace TxtBasedBlog.Sample.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
var manager = new BlogFileSystemManager(Server.MapPath(ConfigurationManager.AppSettings["BlogPostsDirectory"]));
var model = manager.GetBlogListings(5);
return View(model);
}

public ActionResult Error()
{
return View();
}
}
}


最后,是Views/Home/Index.cshtml的代码,注意我们使用的root级的URL,需要在下步定义一些Routes。

@model IEnumerable<TxtBasedBlog.Sample.Models.BlogListing>

<h2>Recent Blog Posts</h2>

@{
foreach (var item in Model)
{
<div class="post">
<div class="img-post">
<a href="/@item.Url" title="@item.Title"><img src="../../@item.Image" alt="" /></a>
</div>
<div class="inline">
<p><a href="@item.Url">@item.PostDate.ToString("MM/dd/yyyy") - @item.Title</a><br />@Html.Raw(item.ShortDescription)</p>
</div>
</div>
}
}


现在,运行网站,如下图:

placeholder 截图

步骤7:显示博客内容

在Global.asax.cs定义一些Routes:

public static void RegisterRoutes(RouteCollection routes)
{
...

routes.MapRoute(
"HomePage",
"",
new { controller = "Home", action = "Index" }
);

routes.MapRoute(
"Error",
"Oops",
new { controller = "Home", action = "Error" }
);

routes.MapRoute(
"BlogPost",
"{postName}",
new { controller = "Home", action = "ViewBlogPost", postName = "" }
);

...
}


当用户点击一个博客链接时,调用Action方法
ViewBlogPost
,在HomeController中添加
ViewBlogPost
有关的代码。

namespace TxtBasedBlog.Sample.Controllers
{
public class HomeController : Controller
{
...

public ActionResult ViewBlogPost(string postName)
{
var manager = new BlogFileSystemManager(Server.MapPath(ConfigurationManager.AppSettings["BlogPostsDirectory"]));
if (!manager.BlogPostFileExistsByTitleForUrl(postName))
{
return RedirectToRoute("Error");
}
var model = manager.GetBlogPostByTitleForUrl(postName);
return View(model);
}

...
}
}
添加
BlogFileSystemManager
来确保获得有效的数据文件。

namespace TxtBasedBlog.Sample.Models
{
public class BlogFileSystemManager
{
...

public bool BlogPostFileExistsByTitleForUrl(string titleForUrl)
{
var matchingFiles = getFilesForBlogPostByTitleForUrl(titleForUrl);
return (matchingFiles.Count == 2);
}

public BlogPost GetBlogPostByTitleForUrl(string titleForUrl)
{
var matchingFiles = getFilesForBlogPostByTitleForUrl(titleForUrl);
var summaryFileData = File.ReadAllText(matchingFiles.Where(i => i.Contains("_summary")).FirstOrDefault());
var blogPost = new JavaScriptSerializer().Deserialize(summaryFileData);
blogPost.Body = File.ReadAllText(matchingFiles.Where(i => !i.Contains("_summary")).FirstOrDefault());
return blogPost;
}

private List getFilesForBlogPostByTitleForUrl(string titleForUrl)
{
// Updated 2012-03-07:
// Richard Fawcett's regex suggestion to prevent titleForUrl subset results. Thanks Richard!
var files = Directory.GetFiles(filePathToBlogPosts, string.Format("*{0}*.txt", titleForUrl));
var r = new Regex(@"\d{4}-\d{2}-\d{2}_" + titleForUrl + @"(_summary)?\.txt", RegexOptions.IgnoreCase);
return files.Where(f => r.IsMatch(f)).ToList();
}

...
}
}
然后,Views/Home/ViewBlogPost.cshtml负责渲染博客文章页面,我们使用一个自定义的Helper方法,
@Html.Markdown()
来调用之前安装的Markdown库。

@using TxtBasedBlog.Sample.Models
@model TxtBasedBlog.Sample.Models.BlogPost

@{
ViewBag.Title = Model.Title;
}

<h2>@Model.Title</h2>
<p>Posted on @Convert.ToDateTime(Model.PostDate).ToString("dd MMM, yyyy") by @Model.Author - @Html.ActionLink("Back to Blog List", "Index")</p>
@Html.Markdown(Model.Body)
@Html.Markdown()是由 Danny Tuppeny创建,原文见original post

namespace TxtBasedBlog.Sample.Models
{
public static class MarkdownHelper
{
static readonly Markdown MarkdownTransformer = new Markdown();

public static IHtmlString Markdown(this HtmlHelper helper, string text)
{
var html = MarkdownTransformer.Transform(text);
return MvcHtmlString.Create(html);
}
}
}


最终完成,运行截图如下,注意URL已经优化。

placeholder:截图。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: