How to generate documentation for Asp.Net MVC?

With .net 4.0/Preview kit 2, we can generate help pages for WCF REST.

Is there anyway we can do the same for MVC ?

www.somewebsite.com/search/help

I can create help pages (views) and expose them.

I can generate XSD schema and spit out as xml.

Any other guidance/suggestions ?

I want to generate something similar to this.

UriTemplate http://somewebsite.com/Search/

Method PUT

Response Format Xml

Response Schema http://somewebsite.com/help/response/schema

Response Example http://somewebsite.com/help/response/example

Update:

I was able to create documentation by using below code.

Route : somewebsite.com/Media/
HelpRoute : somewebsite.com/Media/Help (Append help to the parent route)

Add routes accordingly. See below example.

routes.MapRoute("Search",
           "Search/Quick/",
           new { controller = "Search", action = "Search" },
           new { httpMethod = new HttpMethodConstraint("PUT") }
           );

        routes.MapRoute("SearchHelp",
          "Search/Quick/Help",
          new { controller = "Search", action = "Search" },
          new { helpConstraint = new HelpConstraint { Help = true, SampleType = typeof(Search) } }
          );

 public class HelpResult : ViewResult
{
    private string HelpPage { get; set; }
    private string folderName = "HelpFiles";
    private HttpServerUtilityBase server { get; set; }
    private Type sampleType { get; set; }
    private string schemaName { get; set; }

    const string htmlFormat =
          @"
        
        
            Help
        
                 

        
  • URL - {0}
  • Verb - {1}
  • {2} "; public override void ExecuteResult(ControllerContext context) { server = context.HttpContext.Server; StringBuilder parentUrl = new StringBuilder(); var data = context.RouteData.Route.GetRouteData(context.HttpContext); //Getting requested route url. string url = ((Route)(data.Route)).Url; //Getting parent route from requested route url by removing "Help" from the route. string newUrl = url.Substring(0, url.Length - 4); parentUrl.Append("/" + newUrl); sampleType = data.GetSampleType(); var validVerbs = GetValidVerbs(MakeAppRelative(parentUrl.ToString())); CreateSchema(sampleType, true); HelpPage = string.Format(htmlFormat, newUrl, validVerbs, CreateInputSampleText()); context.HttpContext.Response.Output.Write(HelpPage); } private string CreateInputSampleText() { if (sampleType != null && !string.IsNullOrEmpty(sampleType.Name)) { string sampleText = @"
  • Input Sample Xml - Click Here
  • Input Sample Json - Click Here
  • Input Schema - Click Here
  • "; sampleText = string.Format(sampleText, sampleType.Name, schemaName); return sampleText; } return string.Empty; } private static string MakeAppRelative(string url) { if (!url.StartsWith("~")) { if (!url.StartsWith("/")) url = "~/" + url; else url = "~" + url; } return url; } private static string GetValidVerbs(string Url) { StringBuilder validVerbs = new StringBuilder(); var httpMethodOptions = new[] { "GET", "POST", "PUT", "DELETE", "HEAD" }; foreach (var httpMethodOption in httpMethodOptions) { var fakeContext = new FakeHttpContext(MakeAppRelative(Url), httpMethodOption); foreach (Route route in RouteTable.Routes) { var rd = route.GetRouteData(fakeContext); if (rd != null) { bool errorControllerApplied = route.IsErrorController(); if (!errorControllerApplied) { validVerbs.Append(httpMethodOption); } } } } return validVerbs.ToString(); } private void CreateFile(Type type, Stream stream) { using (Stream inputStream = stream) { schemaName = sampleType + "Schema.xml"; string folder = Path.GetFullPath(Path.Combine(server.MapPath("~"), folderName)); string file = Path.Combine(folder, schemaName); if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); using (FileStream fileStream = new FileStream(file, FileMode.Create)) { byte[] fileContent = new byte[inputStream.Length]; inputStream.Read(fileContent, 0, fileContent.Length); fileStream.Write(fileContent, 0, fileContent.Length); fileStream.Flush(); } } } private void CreateSchema(Type type, bool isXmlSerializerType) { System.Collections.IEnumerable schemas; if (isXmlSerializerType) { XmlReflectionImporter importer = new XmlReflectionImporter(); XmlTypeMapping typeMapping = importer.ImportTypeMapping(type); XmlSchemas s = new XmlSchemas(); XmlSchemaExporter exporter = new XmlSchemaExporter(s); exporter.ExportTypeMapping(typeMapping); schemas = s.GetSchemas(null); } else { XsdDataContractExporter exporter = new XsdDataContractExporter(); exporter.Export(type); schemas = exporter.Schemas.Schemas(); } using (MemoryStream stream = new MemoryStream()) { XmlWriterSettings xws = new XmlWriterSettings() { Indent = true }; using (XmlWriter w = XmlWriter.Create(stream, xws)) { w.WriteStartElement("Schemas"); foreach (XmlSchema schema in schemas) { if (schema.TargetNamespace != "http://www.w3.org/2001/XMLSchema") { schema.Write(w); } } } stream.Seek(0, SeekOrigin.Begin); CreateFile(type, stream); } } public static class RouteDataExtensions { public static bool IsHelpConstraintApplied(this RouteData data) { if (data != null && data.Route != null) { var constraints = ((Route) (data.Route)).Constraints; var helpConstraint = (from c in constraints.Values where c.GetType().Equals(typeof (HelpConstraint)) select c).FirstOrDefault(); if (helpConstraint != null) { return true; } } return false; } public static Type GetSampleType(this RouteData data) { if (data != null && data.Route != null) { var constraints = ((Route)(data.Route)).Constraints; var helpConstraint = (from c in constraints.Values where c.GetType().Equals(typeof(HelpConstraint)) select c).FirstOrDefault(); if (helpConstraint != null) { return ((HelpConstraint) helpConstraint).SampleType; } } return null; } public static string GetMethodType(this RouteData data) { if (data != null && data.Route != null) { var constraints = ((Route) (data.Route)).Constraints; var httpMethodConstraint = (from c in constraints.Values where c.GetType().Equals(typeof (HttpMethodConstraint)) select c).FirstOrDefault(); if (httpMethodConstraint != null) { return ((HttpMethodConstraint) httpMethodConstraint).AllowedMethods.Single(); } } return null; } public static bool IsErrorController(this Route data) { if (data != null) { var defaults = ((Route)(data)).Defaults; var controllerName = (from c in defaults.Values where c.ToString().Contains("Error") select c).FirstOrDefault(); if (controllerName != null) { return true; } } return false; } public static RouteData GetRouteDataByUrl(this string url) { string httpMethod = "PUT"; var fakeContext = new FakeHttpContext(MakeAppRelative(url), httpMethod); return RouteTable.Routes.GetRouteData(fakeContext); } private static string MakeAppRelative(string url) { if (!url.StartsWith("~")) { if (!url.StartsWith("/")) url = "~/" + url; else url = "~" + url; } return url; } } public class HelpConstraint : IRouteConstraint { public bool Help { get; set; } public Type SampleType { get; set; } public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { if(Help) { return true; } return false; } } References http://stephenwalther.com/blog/archive/2008/08/03/asp-net-mvc-tip-29-build-a-controller-to-debug-your-custom-routes.aspx http://aspnet.codeplex.com/releases/view/24644

    This code is not bug free. Please post improvements if you have any. Use it at your own risk.

    6
    задан Abhilash 22 April 2011 в 19:10
    поделиться