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 =
@"
<html>
<head>
<title>Help</title>
</head>
<body>
<li>URL - {0}</li>
<li>Verb - {1}</li>
{2}
</body>
</html>
";
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 =
@"<li>Input Sample Xml - <a href='\HelpFiles\{0}.xml'>Click Here</a></li>
<li>Input Sample Json - <a href='\HelpFiles\{0}.txt'>Click Here</a></li>
<li>Input Schema - <a href='\HelpFiles\{1}'>Click Here</a></li>";
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.
Not exactly sure what you are looking for, but you can check out GhostDoc:
http://submain.com/products/ghostdoc.aspx
I use this to generate XML documentation in MVC.
Most probably you have solved your issue by now. Anyway, I think you need IApiExplorer. Have a look at this blog.
来源:https://stackoverflow.com/questions/5667305/how-to-generate-documentation-for-asp-net-mvc