5
\$\begingroup\$

I recently added a feature to my application to serve arbitrary markdown documents from a directory. This is designed to allow authors to populate a folder full of help documents, and be able to view them without any code changes. However, because this is an MVC application, I needed a few extra features. Straight to it, here's my controller:

public class DocumentationController : Controller
{
 private const string DefaultDocPage = "Overview";
 public ActionResult Index(string id = null)
 {
 ViewBag.HomeLinkPage = string.IsNullOrEmpty(id) || id == DefaultDocPage ? string.Empty : DefaultDocPage;
 if (string.IsNullOrEmpty(ViewBag.HomeLinkPage))
 {
 id = DefaultDocPage;
 }
 var filePath = Server.MapPath(Url.Content("~/Content/Documentation/" + id.Trim("/".ToCharArray()) + ".md"));
 if(!System.IO.File.Exists(filePath))
 {
 Response.StatusCode = (int)HttpStatusCode.NotFound;
 return null;
 }
 var contents = new StringBuilder(System.IO.File.ReadAllText(filePath));
 contents.Replace("{{AppRoot}}/", Url.Content("~"));
 contents.Replace("{{Documentation}}", Url.Content("~/Documentation"));
 contents.Replace("{{DocumentationImages}}", Url.Content("~/Content/Documentation/images"));
 contents.Replace("{{SupportLink}}", ConfigurationManager.AppSettings["SupportLink"]);
 return View("Index", (object)contents.ToString());
 }
}

In short, it allows passing a file name (without ".md") called "id" (poor name is my fault for lazy routing), assigns a default document if a file name was not passed, sets a ViewBag link to the homepage if we're not on the default document, maps the file name to a markdown document in the directory, replaces some preprocessed keywords, and returns the view with the contents of the document.

My view renders the homepage link, if applicable, then the document using MarkdownSharp. I haven't seen my idea of "preprocessed keywords" in markdown before, so I was wandering what others think. It's really useful for resolving links in documents, without worrying about them while writing. It allows document writers to write:

To view your user page [click here]({{AppRoot}}/User/Me) and here's an image: ![image]({{DocumentationImages}}/UserPageImage.png)

Which is transformed to this, on localhost, without the author worrying about the actual path to the application, or the content directory:

To view your user page [click here](http://localhost/MyApp/User/Me) and here's an image: ![image](http://localhost/MyApp/Content/Documentation/images/UserPageImage.png)

I also added one for the support link, so I could pull in that setting from ConfigurationManager.AppSettings.

My questions, succinctly:

  • Are there any realistic cases that I may not be anticipating?
  • Is there a best practice that I'm not aware of, for having pre-processed text replaced in markdown?
  • Is there a better way than StringBuilder.Replace for processing each of my keywords?
Ethan Bierlein
15.9k4 gold badges59 silver badges146 bronze badges
asked Mar 31, 2014 at 19:01
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

A few minor nit-picks:

  1. I'd change the id checking code around a bit to avoid some redundant checks by first sanitizing the input:

    id = string.IsNullOrEmpty(id) ? DefaultDocPage : id;
    ViewBag.HomeLinkPage = id == DefaultDocPage ? string.Empty : DefaultDocPage;
    
  2. Consider making DefaultDocPage a configurable property so you don't have to change the code (削除) if (削除ここまで) when someone asks to have a different name

  3. Trim takes a params array so there is no need to do the ToCharArray, you can just pass in all the trim characters directly like this:

    id.Trim('/')
    
  4. Replacing

    var filePath = Server.MapPath(Url.Content("~/Content/Documentation/" + id.Trim("/".ToCharArray()) + ".md"));
    

    with string.format makes it a bit easier to read:

    var filePath = Server.MapPath(Url.Content(string.format("~/Content/Documentation/{0}.md", id.Trim('/'))));
    

(削除) 1. There is no need to cast to object here:

return View("Index", (object)contents.ToString());

(削除ここまで)

answered Mar 31, 2014 at 21:07
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Thanks! For #4 the cast to object is actually necessary when passing a string here, otherwise MVC tries to use a different overload that uses "Index" as the view name and the passed string as the Master view name. There are other ways to solve that though, that may be more readable. \$\endgroup\$ Commented Mar 31, 2014 at 21:11
  • \$\begingroup\$ Ah, alright, I'm not doing much MVC so I'm not aware \$\endgroup\$ Commented Mar 31, 2014 at 21:12

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.