ASP Web API帮助页面 – 从XML 标记链接到类

我正在开发一个Web-API项目,我对微软的HelpPages自动生成的文档印象非常深刻。

我使用官方网站创建帮助页面启用自定义文档

文档生成成功但是标签中的类的引用似乎都没有添加到描述中,HelpPages只是忽略它们(这是有原因的)。

我真的很想在我的项目中使用这个function,我搜索了很多(有时候很近),但没有一个给出令人信服的答案。

这就是为什么我决定将我的解决方案发布到这个调整,并希望有利于其他程序员,并节省他们一些时间和精力。 (我的回答在下面的回复中)

我的解决方案如下:

  1. 你有自定义文档(来自生成的xml文件)工作
  2. 在文档中启用HTML和XML标记,它们通常会被过滤掉,感谢这篇post你可以保留它们。
    只需转到:ProjectName>区域> HelpPage> XmlDocumentationProvider.cs
    在方法的第123行:GetTagValue(XPathNavigator parentNode,string tagName)
    更改代码return node.Value.Trim(); return node.InnerXml;
  3. 创建以下部分视图:
    项目名\区\ HelpPage \意见\帮助** _ ** XML_SeeTagsRenderer.cshtml
    这是我的代码:
 @using System.Web.Http; @using MyProject.Areas.HelpPage.Controllers; @using MyProject.Areas.HelpPage; @using MyProject.Areas.HelpPage.ModelDescriptions @using System.Text.RegularExpressions @model string 
 @{ int @index = 0; string @xml = Model; if (@xml == null) @xml = ""; Regex @seeRegex = new Regex("<( *)see( +)cref=\"([^\"]):([^\"]+)\"( *)/>");//Regex(""); Match @xmlSee = @seeRegex.Match(@xml); string @typeAsText = ""; Type @tp; ModelDescriptionGenerator modelDescriptionGenerator = (new HelpController()).Configuration.GetModelDescriptionGenerator(); } @if (xml !="" && xmlSee != null && xmlSee.Length > 0) { while (xmlSee != null && xmlSee.Length > 0) { @MvcHtmlString.Create(@xml.Substring(@index, @xmlSee.Index - @index)) int startingIndex = xmlSee.Value.IndexOf(':')+1; int endIndex = xmlSee.Value.IndexOf('"', startingIndex); typeAsText = xmlSee.Value.Substring(startingIndex, endIndex - startingIndex); //.Replace("", ""); System.Reflection.Assembly ThisAssembly = typeof(ThisProject.Controllers.HomeController).Assembly; tp = ThisAssembly.GetType(@typeAsText); if (tp == null)//try another referenced project { System.Reflection.Assembly externalAssembly = typeof(MyExternalReferncedProject.AnyClassInIt).Assembly; tp = externalAssembly.GetType(@typeAsText); } if (tp == null)//also another referenced project- as needed { System.Reflection.Assembly anotherExtAssembly = typeof(MyExternalReferncedProject2.AnyClassInIt).Assembly; tp = anotherExtAssembly .GetType(@typeAsText); } if(tp == null)//case of nested class { System.Reflection.Assembly thisAssembly = typeof(ThisProject.Controllers.HomeController).Assembly; //the below code is done to support detecting nested classes. var processedTypeString = typeAsText; var lastIndexofPoint = typeAsText.LastIndexOf('.'); while (lastIndexofPoint > 0 && tp == null) { processedTypeString = processedTypeString.Insert(lastIndexofPoint, "+").Remove(lastIndexofPoint + 1, 1); tp = SPLocatorBLLAssembly.GetType(processedTypeString);//nested class are recognized as: namespace.outerClass+nestedClass lastIndexofPoint = processedTypeString.LastIndexOf('.'); } } if (@tp != null) { ModelDescription md = modelDescriptionGenerator.GetOrCreateModelDescription(tp); @Html.DisplayFor(m => md.ModelType, "ModelDescriptionLink", new { modelDescription = md }) } else { @MvcHtmlString.Create(@typeAsText) } index = xmlSee.Index + xmlSee.Length; xmlSee = xmlSee.NextMatch(); } @MvcHtmlString.Create(@xml.Substring(@index, @xml.Length - @index)) } else { @MvcHtmlString.Create(@xml); } 
  1. 最后转到:ProjectName \ Areas \ HelpPage \ Views \ Help \ DisplayTemplates ** Parameters.cshtml **
    在第20行,我们有与文档中的描述相对应的代码。
    替换这个:

       

    @parameter.Documentation

有了这个:

   

@Html.Partial("_XML_SeeTagsRenderer", (@parameter.Documentation == null? "" : @parameter.Documentation.ToString()))

&Voila你现在必须让它工作。
笔记:

  • 我尝试将HTML列表放在文档中,它使它很好
  • 我尝试了多个类引用(多个 ,我工作得很好
  • 你不能引用在类中声明的类
  • 当你引用一个在当前项目之外的类时,请在该项目中添加一个类的程序集.getType(查看上面的代码)
  • 找到的任何未找到的类将在描述中打印出它的全名(例如,如果引用属性或命名空间,代码将不会将其标识为类型/类,但它将被打印)

我已经实现了处理xml文档块的类,并将文档标记更改为html标记。

 ///  /// Reprensets xml help block converter interface. ///  public class HelpBlockRenderer : IHelpBlockRenderer { ///  /// Stores regex to parse See tag. ///  private static readonly Regex regexSeeTag = new Regex("<( *)see( +)cref=\"(?[^\"]):(?[^\"]+)\"( *)/>", RegexOptions.IgnoreCase); ///  /// Stores the pair tag coversion dictionary. ///  private static readonly Dictionary pairedTagConvertsion = new Dictionary() { { "para", "p" }, { "c", "b" } }; ///  /// Stores configuration. ///  private HttpConfiguration config; ///  /// Initializes a new instance of the  class. ///  /// The configuration. public HelpBlockRenderer(HttpConfiguration config) { this.config = config; } ///  /// Initializes a new instance of the  class. ///  public HelpBlockRenderer() : this(GlobalConfiguration.Configuration) { } ///  /// Renders specified xml help block to valid html content. ///  /// The help block. /// The url helper for link building. /// The html content. public HtmlString RenderHelpBlock(string helpBlock, UrlHelper urlHelper) { if (string.IsNullOrEmpty(helpBlock)) { return new HtmlString(string.Empty); } string result = helpBlock; result = this.RenderSeeTag(result, urlHelper); result = this.RenderPairedTags(result); return new HtmlString(result); } ///  /// Process See tag. ///  /// Hte original help block string. /// The url helper for link building. /// The html content. private string RenderSeeTag(string helpBlock, UrlHelper urlHelper) { string result = helpBlock; Match match = null; while ((match = HelpBlockRenderer.regexSeeTag.Match(result)).Success) { var originalValues = match.Value; var prefix = match.Groups["prefix"].Value; var anchorText = string.Empty; var link = string.Empty; switch (prefix) { case "T": { // if See tag has reference to type, then get value from member regex group. var modelType = match.Groups["member"].Value; anchorText = modelType.Substring(modelType.LastIndexOf(".") + 1); link = urlHelper.Action("ResourceModel", "Help", new { modelName = anchorText, area = "ApiHelpPage" }); break; } case "M": { // Check that specified type member is API member. var apiDescriptor = this.GetApiDescriptor(match.Groups["member"].Value); if (apiDescriptor != null) { anchorText = apiDescriptor.ActionDescriptor.ActionName; link = urlHelper.Action("Api", "Help", new { apiId = ApiDescriptionExtensions.GetFriendlyId(apiDescriptor), area = "ApiHelpPage" }); } else { // Web API Help can generate help only for whole API model, // So, in case if See tag contains link to model member, replace link with link to model class. var modelType = match.Groups["member"].Value.Substring(0, match.Groups["member"].Value.LastIndexOf(".")); anchorText = modelType.Substring(modelType.LastIndexOf(".") + 1); link = urlHelper.Action("ResourceModel", "Help", new { modelName = anchorText, area = "ApiHelpPage" }); } break; } default: { anchorText = match.Groups["member"].Value; // By default link will be rendered with empty anrchor. link = "#"; break; } } // Build the anchor. var anchor = string.Format("{1}", link, anchorText); result = result.Replace(originalValues, anchor); } return result; } ///  /// Converts original help paired tags to html tags. ///  /// The help block. /// The html content. private string RenderPairedTags(string helpBlock) { var result = helpBlock; foreach (var key in HelpBlockRenderer.pairedTagConvertsion.Keys) { Regex beginTagRegex = new Regex(string.Format("<{0}>", key), RegexOptions.IgnoreCase); Regex endTagRegex = new Regex(string.Format("", key), RegexOptions.IgnoreCase); result = beginTagRegex.Replace(result, string.Format("<{0}>", HelpBlockRenderer.pairedTagConvertsion[key])); result = endTagRegex.Replace(result, string.Format("", HelpBlockRenderer.pairedTagConvertsion[key])); } return result; } ///  /// Gets the api descriptor by specified member name. ///  /// The member fullname. /// The api descriptor. private ApiDescription GetApiDescriptor(string member) { Regex controllerActionRegex = new Regex("[a-zA-Z0-9\\.]+\\.(?[a-zA-Z0-9]+)Controller\\.(?[a-zA-Z0-9]+)\\(.*\\)"); var match = controllerActionRegex.Match(member); if (match.Success) { var controller = match.Groups["controller"].Value; var action = match.Groups["action"].Value; var descriptions = this.config.Services.GetApiExplorer().ApiDescriptions; return descriptions.FirstOrDefault(x => x.ActionDescriptor.ActionName.Equals(action) && x.ActionDescriptor.ControllerDescriptor.ControllerName == controller); } return null; } } 

要使用它,您需要更改XmlDocumentationProvider类:

 private static string GetTagValue(XPathNavigator parentNode, string tagName) { if (parentNode != null) { XPathNavigator node = parentNode.SelectSingleNode(tagName); if (node != null) { return node.InnerXml; } } return null; } 

然后我写了扩展类来直接从视图中使用这个类:

 ///  /// Represents html help content extension class. /// Contains methods to convert Xml help blocks to html string. ///  public static class HtmlHelpContentExtensions { ///  /// Converts help block in xml format to html string with proper tags, links and etc. ///  /// The help block content. /// The url helper for link building. /// The resulting html string. public static HtmlString ToHelpContent(this string helpBlock, UrlHelper urlHelper) { // Initialize your rendrer here or take it from IoC return renderer.RenderHelpBlock(helpBlock, urlHelper); } } 

最后,例如在Parameters.cshtml中:

  

@parameter.Documentation.ToHelpContent(Url)

@if(!string.IsNullOrEmpty(parameter.Remarks)) {

@parameter.Remarks.ToHelpContent(Url)

}