ASP Web API Help pages - Links to other pages

2019-02-28 14:30发布

问题:

I am using Web API help pages and I would like to be able to include links to other methods. I have seen from Are XML documentation tags not being handled by Web API 2 help pages? that using <see cref='...'> isn't supported.

Are there any better options than writing my own <a href='...'> links in the documentation, and using the method described in Web Api Help Page- don't escape html in xml documentation to get these <a> tags output to the help? It seems very brittle to any future changes.

The only alternative I could think of would be to force the API explorer to run twice - once to cache all the different routes & their corresponding help page URLs, and a second time to actually generate the documentation. But I'm not sure where to hook this in (or if it's even possible)

回答1:

I have managed to write something that correctly converts links.

An outline is:

In the Help Controller's constructor create a new Lazy IDictionary mapping between the strings found in the cref (e.g. M:Api.Method.Description(System.String) and the associated ApiDescription

        _methodReferences = new Lazy<IDictionary<string, ApiDescription>>(() => {
            var dictionary = new Dictionary<string, ApiDescription>();

            var apiExplorer = new ApiExplorer(config);

            foreach (var apiDescription in apiExplorer.ApiDescriptions)
            {
                var descriptor = apiDescription.ActionDescriptor as ReflectedHttpActionDescriptor;
                if (descriptor != null)
                {
                    var methodName = string.Format(
                        @"M:{0}.{1}({2})",
                        descriptor.MethodInfo.DeclaringType.FullName,
                        descriptor.MethodInfo.Name,
                        string.Join(@",",descriptor.GetParameters().Select(x => x.ParameterType.FullName))
                        );
                    dictionary[methodName] = apiDescription;
                }

            }
            return dictionary;
        });

Pass this lazy to the various models that back the pages (you may need to create extra models). I have given them all a base class with the following code:

public abstract class HelpPageModelBase
{
    private static Regex _seeRegex = new Regex("<see cref=\"([^\"]+)\" />");
    private readonly Lazy<IDictionary<string, ApiDescription>> _methodReferences;

    protected HelpPageModelBase(Lazy<IDictionary<string, ApiDescription>> methodReferences)
    {
        _methodReferences = methodReferences;
    }

    protected HelpPageModelBase(HelpPageModelBase parent)
    {
        _methodReferences = parent._methodReferences;
    }

    public string ParseDoc(string documentation, UrlHelper url)
    {
        if (documentation == null)
        {
            return null;
        }
        return _seeRegex.Replace(documentation,
                                 match => {
                                     if (_methodReferences.Value.ContainsKey(match.Groups[1].Value))
                                     {
                                         var descriptor = _methodReferences.Value[match.Groups[1].Value];

                                         return string.Format(@"<a href='{0}'>{1} {2}</a>",
                                                              url.Action("Api",
                                                                         "Help",
                                                                         new {
                                                                             apiId = descriptor.GetFriendlyId()

                                                                         }),
                                                              descriptor.HttpMethod.Method,
                                                              descriptor.RelativePath
                                             );
                                     }
                                     return "";
                                 });
    }
}

Anywhere in the views that had api.Documentation.Trim() - or Html.Raw(api.Documentation) if you have already followed Web Api Help Page- don't escape html in xml documentation - you now wrap so it becomes

@Html.Raw(Model.ParseDoc(api.Documentation, Url))

You will find that to do this you need to make the various ModelDescriptions inherit from HelpPageModelBase - and pass the parent API models through to them (or the Lazy if easier) but it does work in the end.

I'm not especially happy with this solution; you might find it easier to have some form of static ParseDoc method that uses the default Http Configuration to generate the Lazy (but because of other extensions I've made that doesn't work for my case). If you see better ways of doing it, please share! Hopefully it gives you a starting point.