web-dev-qa-db-ja.com

ASP.NET Web APIモデルからすべてのパラメーターを生成する-ヘルプページ

Web API(asp mvc4アプリケーション内)の作成に忙しい。ドキュメントを生成するためにasp.netサイトで提案されているライブラリを使用しています( http://www.asp.net/web-api/overview/creating-web-apis/creating-api-help-pages )。

私の問題は、パラメーターがモデルの場合、生成されたヘルプページでモデルに含まれるプロパティを指定できないことです。

次に例を示します。

モデル:

public class TestModel
{
    property String FirstName {get;set;}
    property String Surname {get; set;}
    property Boolean Active {get;set;} 
}

アクション:

/// <summary>
/// This is a test action
/// </summary>
/// <param name="model">this is the model</param> <-- this works
/// <param name="FirstName">This is the first name </param>  <-- doesn't work
/// <param name ="model.Surname">This is the surname</param> <-- doesn't work
public HttpResponseMessage Post(my.namespace.models.TestModel model)
{
  ...
}

モデルのパラメーターのみが生成されます。

ドキュメント用に生成されたxmlドキュメントを確認しましたが、他のパラメータが追加されています。

<member name="my.namespace.api.Post(my.namespace.models.TestModel)">
     <summary>
         this is a test action
     </summary>
     <param name="model>this is the model</param>
     <param name="FirstName">This is the first name </param>
     <param name="model.Surname">This is the surname</param>
</member>

ただし、ヘルプページでは、パラメータモデルのみが生成されます。

私はそれをxmlからパラメーターを取得するメソッドまで追跡しました。

Collection<ApiDescription> apiDescriptions = config.Services.GetApiExplorer().ApiDescriptions;

これは、自動生成されるHelpPageConfigurationExtentions.csにあります。

私はこれを間違った方法でアプローチしていますか?誰かが回避策を知っていますか?

提案や解決策はありがたいです。

23
Jeandre Pentz

MVC Web APIドキュメント機能は、リフレクションを使用してAPIクラスとメソッドをウォークスルーします。これにより、ドキュメントの構造が構築されますが、ドキュメントのコメントを追加しない限り、ドキュメントは空(または役に立たない)になります。

ドキュメンテーションの本文は、///ドキュメンテーションコメントを使用して生成されるXMLファイルを使用して入力されます。このコメントには、従う必要がある特定の構造があります。つまり、表示したいものでxmlを埋めることはできません。実際には、API内の何かに接続する必要があり、クラスとプロパティの構造に従う必要があります。

したがって、あなたのケースでは、apiプロパティにモデルプロパティのドキュメントを置くことはできません。プロパティが存在するモデルにそれを置く必要があります。

モデル:

  public class TestModel
  {
  /// <summary>This is the first name </summary>
      property String FirstName {get;set;}
  /// <summary>This is the surname</summary>
      property String Surname {get; set;}
      property Boolean Active {get;set;} 
  }

アクション:

  /// <summary>
  /// This is a test action
  /// </summary>
  /// <param name="model">this is the model</param> 
  public HttpResponseMessage Post(my.namespace.models.TestModel model)
  {
    ...
  }

ヘルプページの変更

自動的に生成されるデフォルトのヘルプページには、モデルのドキュメントは含まれていません。APIメソッドのみがドキュメント化されています。 APIのパラメーターに関する詳細情報を表示するには、カスタマイズが必要です。次の手順は、パラメータドキュメントを追加する1つの方法です。

Areas/HelpPage/Modelsに2つの新しいタイプを作成します

public class TypeDocumentation
{
    public TypeDocumentation()
    {
        PropertyDocumentation = new Collection<PropertyDocumentation>();
    }

    public string Summary { get; set; }
    public ICollection<PropertyDocumentation> PropertyDocumentation { get; set; } 
}

public class PropertyDocumentation
{
    public PropertyDocumentation(string name, string type, string docs)
    {
        Name = name;
        Type = type;
        Documentation = docs;
    }
    public string Name { get; set; }
    public string Type { get; set; }
    public string Documentation { get; set; }
}

HelpPageApiModel.csに新しいプロパティを追加する

public IDictionary<string, TypeDocumentation> ParameterModels{ get; set; } 

新しいインターフェースを作成する

internal interface IModelDocumentationProvider
{
    IDictionary<string, TypeDocumentation> GetModelDocumentation(HttpActionDescriptor actionDescriptor);
}

XmlDocumentationProviderを変更して新しいインターフェイスを実装する

public class XmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider
{
    private const string TypeExpression = "/doc/members/member[@name='T:{0}']";
    private const string PropertyExpression = "/doc/members/member[@name='P:{0}']";
///...
///... existing code
///...

    private static string GetPropertyName(PropertyInfo property)
    {
        string name = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", property.DeclaringType.FullName, property.Name);
        return name;
    }

    public IDictionary<string, TypeDocumentation> GetModelDocumentation(HttpActionDescriptor actionDescriptor)
    {
        var retDictionary = new Dictionary<string, TypeDocumentation>();
        ReflectedHttpActionDescriptor reflectedActionDescriptor = actionDescriptor as ReflectedHttpActionDescriptor;
        if (reflectedActionDescriptor != null)
        {
            foreach (var parameterDescriptor in reflectedActionDescriptor.GetParameters())
            {
                if (!parameterDescriptor.ParameterType.IsValueType)
                {
                    TypeDocumentation typeDocs = new TypeDocumentation();


                    string selectExpression = String.Format(CultureInfo.InvariantCulture, TypeExpression, GetTypeName(parameterDescriptor.ParameterType));
                    var typeNode = _documentNavigator.SelectSingleNode(selectExpression);

                    if (typeNode != null)
                    {
                        XPathNavigator summaryNode;
                        summaryNode = typeNode.SelectSingleNode("summary");
                        if (summaryNode != null)
                            typeDocs.Summary = summaryNode.Value;
                    }

                    foreach (var prop in parameterDescriptor.ParameterType.GetProperties())
                    {
                        string propName = prop.Name;
                        string propDocs = string.Empty;
                        string propExpression = String.Format(CultureInfo.InvariantCulture, PropertyExpression, GetPropertyName(prop));
                        var propNode = _documentNavigator.SelectSingleNode(propExpression);
                        if (propNode != null)
                        {
                            XPathNavigator summaryNode;
                            summaryNode = propNode.SelectSingleNode("summary");
                            if (summaryNode != null) propDocs = summaryNode.Value;
                        }
                        typeDocs.PropertyDocumentation.Add(new PropertyDocumentation(propName, prop.PropertyType.Name, propDocs));

                    }
                    retDictionary.Add(parameterDescriptor.ParameterName, typeDocs);
                }

            }

        }

        return retDictionary;
    }
}

GenerateApiModelメソッドのHelpPageConfigurationExtensionにコードを追加します。

IModelDocumentationProvider modelProvider =
            config.Services.GetDocumentationProvider() as IModelDocumentationProvider;
if (modelProvider != null)
{
    apiModel.ParameterModels = modelProvider.GetModelDocumentation(apiDescription.ActionDescriptor);
}

HelpPageApiModel.cshtmlを変更して、モデルのドキュメントを表示する場所を以下に追加します。

bool hasModels = Model.ParameterModels.Count > 0;
if (hasModels)
{
     <h2>Parameter Information</h2>
  @Html.DisplayFor(apiModel => apiModel.ParameterModels, "Models")

}

DisplayTemplatesにModels.cshtmlを追加します

@using System.Web.Http
@using System.Web.Http.Description
@using MvcApplication2.Areas.HelpPage.Models
@model IDictionary<string, TypeDocumentation>

@foreach (var modelType in Model)
{
    <h3>@modelType.Key</h3>
    if (modelType.Value.Summary != null)
    {
    <p>@modelType.Value.Summary</p>
    }
    <table class="help-page-table">
        <thead>
            <tr>
                <th>Property</th>

                <th>Description</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var propInfo in modelType.Value.PropertyDocumentation)
            {
                <tr>
                    <td class="parameter-name"><b>@propInfo.Name</b> (@propInfo.Type)</td>

                    <td class="parameter-documentation">
                        <pre>@propInfo.Documentation</pre>
                    </td>
                </tr>
            }
        </tbody>
    </table>
}
27
agilejoshua

josantの答えはうまくいきます。しかし、それは少し熱心すぎると思いました。文字列のような単純なものをモデルとして報告し、それらを長さフィールドを持つChar配列であると報告していることがわかりました!

これはモデルにのみ必要なので、このコードをGetModelDocumentationメソッドの最後に追加しました。

if (parameterDescriptor.ParameterName == "value" || parameterDescriptor.ParameterName == "model")
{
    retDictionary.Add(parameterDescriptor.ParameterName, typeDocs);
}

現在は、単純ではない型のパラメータの詳細のみが返されます。

0
Maxcelcat