Search code examples
c#asp.netasp.net-web-apiwsdlservice-discovery

List Of Controllers And Classes In Asp.NET Web API


I want to access all of controllers in my web api. Let's say I have 2 controllers and 2 classes;

Foo:

string fooId

string fooName

Bar:

string barId

string barName

Sample1Controller:

Get(int fooId)

Post([FromBody] Foo foo)

Sample2Controller:

Get(int barId)

Post([FromBody] Bar bar)

I want to list of my controllers, Foo and Bar class with properties. How can I do this ?

Update:

I want to create request and response classes for mobile apps. For example If I access these details remotely, I can create request and response classes for java or objective-c.


Solution

  • You could use the ApiExplorer class which is a class specifically designed to generate documentation for Web APIs.

    Typically it is used to generate HTML help pages but there is nothing to stop you creating a more machine readable output such as JSON or XML. If you expose the output via an API action method you will get either depending on the requested type just like any other API method.

    There is a good article on creating a help page here but there isn't much material around about outputting anything other than HTML. Unfortunately the ApiExplorer classes are not serializable so you can't just return the result of a call to GetApiExplorer() but it's trivial enough to create our own classes that are serializable, populate them and then return those from an API action.

    You can access the ApiExplorer classes by using GlobalConfiguration.Configuration.Services.GetApiExplorer().ApiDescriptions. That will return a Collection<ApiDescription> which contains information on the controllers, actions and parameters. It can even be used to access documentation from any ///summary comments if you so desire. It depends on what information you are after and what format you would like that in but the below is an example of what you can achieve using this method:

    Firstly I've created a class to store the Action method details:

    [DataContract]
    public class ActionMethod
    {
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public List<Parameter> Parameters { get; set; }
        [DataMember]
        public string SupportedHttpMethods { get; set; }
    }
    

    Note that the SupportedHttpMethods is just a string rather than a List<T>. You may want to improve that to a List<T> but for this example I'm just comma separating them to make life slightly easier.

    The ActionMethod class has a List<Parameter> which looks like this:

    [DataContract]
    public class Parameter
    {
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public string Source { get; set; } //where we pass the parameter when calling the action
        [DataMember]
        public string Type { get; set; }
        [DataMember]
        public List<Parameter> SubParameters { get; set; }
    
    }
    

    Note that the SubParameters is for storing the types of member variables for complex types. I'm only capturing one level deep but it would be easy enough to extend this if required.

    Then, I've created a new Controller with an action method that will return our API information as a List<ActionMethod>. Note that I've added the attribute [ApiExplorerSettings(IgnoreApi = true)] which tells the ApiExplorer to ignore anything in this Controller so we don't generate documentation on our documenting controller (that would work, but it's too meta for me!).

    [ApiExplorerSettings(IgnoreApi = true)]
    public class HelpController : ApiController
    {
        public List<ActionMethod> Get()
        {
            var apiActions = new List<ActionMethod>();
    
            Collection<ApiDescription> apiDescriptions = GlobalConfiguration
                               .Configuration
                               .Services
                               .GetApiExplorer()
                               .ApiDescriptions;
    
            foreach (var api in apiDescriptions)
            {
                List<Parameter> parameters = new List<Parameter>();
                //get the parameters for this ActionMethod
                foreach (var parameterDescription in api.ParameterDescriptions)
                {
                    Parameter parameter = new Parameter()
                    {
                        Name = parameterDescription.Name, 
                        Source = parameterDescription.Source.ToString(),
                        Type = parameterDescription.ParameterDescriptor.ParameterType.ToString(),
                        SubParameters = new List<Parameter>()
                    };
                    //get any Sub-Parameters (for complex types; this should probably be recursive)
                    foreach (var subProperty in parameterDescription.ParameterDescriptor.ParameterType.GetProperties())
                    {
                        parameter.SubParameters.Add(new Parameter()
                        {
                            Name = subProperty.Name,
                            Type = subProperty.PropertyType.ToString()
                        });
                    }
    
                    parameters.Add(parameter);
                }
                //add a new action to our list
                apiActions.Add(new ActionMethod()
                {
                    Name = api.ActionDescriptor.ControllerDescriptor.ControllerName,
                    Parameters = parameters, 
                    SupportedHttpMethods = string.Join(",", api.ActionDescriptor.SupportedHttpMethods)
                });
            }
    
            return apiActions;
        }
    }
    

    We can then access the api documentation at /api/help. With the controller and action methods you give as an example in your question, asking for JSON gives a response like this:

    [
       {
          "Name":"Bar",
          "Parameters":[
             {
                "Name":"barId",
                "Source":"FromUri",
                "Type":"System.Int32",
                "SubParameters":[
    
                ]
             }
          ],
          "SupportedHttpMethods":"GET"
       },
       {
          "Name":"Bar",
          "Parameters":[
             {
                "Name":"bar",
                "Source":"FromBody",
                "Type":"ApiTest.Controllers.Bar",
                "SubParameters":[
                   {
                      "Name":"barId",
                      "Source":null,
                      "Type":"System.String",
                      "SubParameters":null
                   },
                   {
                      "Name":"barName",
                      "Source":null,
                      "Type":"System.String",
                      "SubParameters":null
                   }
                ]
             }
          ],
          "SupportedHttpMethods":"POST"
       },
       {
          "Name":"Foo",
          "Parameters":[
             {
                "Name":"fooId",
                "Source":"FromUri",
                "Type":"System.Int32",
                "SubParameters":[
    
                ]
             }
          ],
          "SupportedHttpMethods":"GET"
       },
       {
          "Name":"Foo",
          "Parameters":[
             {
                "Name":"foo",
                "Source":"FromBody",
                "Type":"ApiTest.Controllers.Foo",
                "SubParameters":[
                   {
                      "Name":"fooId",
                      "Source":null,
                      "Type":"System.String",
                      "SubParameters":null
                   },
                   {
                      "Name":"fooName",
                      "Source":null,
                      "Type":"System.String",
                      "SubParameters":null
                   }
                ]
             }
          ],
          "SupportedHttpMethods":"POST"
       }
    ]
    

    and asking for XML gives us:

    <?xml version="1.0" encoding="UTF-8"?>
    <ArrayOfActionMethod xmlns="http://schemas.datacontract.org/2004/07/ApiTest.Controllers" 
                         xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <ActionMethod>
            <Name>Bar</Name>
            <Parameters>
                <Parameter>
                    <Name>barId</Name>
                    <Source>FromUri</Source>
                    <SubParameters />
                    <Type>System.Int32</Type>
                </Parameter>
            </Parameters>
            <SupportedHttpMethods>GET</SupportedHttpMethods>
        </ActionMethod>
        <ActionMethod>
            <Name>Bar</Name>
            <Parameters>
                <Parameter>
                    <Name>bar</Name>
                    <Source>FromBody</Source>
                    <SubParameters>
                        <Parameter>
                            <Name>barId</Name>
                            <Source i:nil="true" />
                            <SubParameters i:nil="true" />
                            <Type>System.String</Type>
                        </Parameter>
                        <Parameter>
                            <Name>barName</Name>
                            <Source i:nil="true" />
                            <SubParameters i:nil="true" />
                            <Type>System.String</Type>
                        </Parameter>
                    </SubParameters>
                    <Type>ApiTest.Controllers.Bar</Type>
                </Parameter>
            </Parameters>
            <SupportedHttpMethods>POST</SupportedHttpMethods>
        </ActionMethod>
        <ActionMethod>
            <Name>Foo</Name>
            <Parameters>
                <Parameter>
                    <Name>fooId</Name>
                    <Source>FromUri</Source>
                    <SubParameters />
                    <Type>System.Int32</Type>
                </Parameter>
            </Parameters>
            <SupportedHttpMethods>GET</SupportedHttpMethods>
        </ActionMethod>
        <ActionMethod>
            <Name>Foo</Name>
            <Parameters>
                <Parameter>
                    <Name>foo</Name>
                    <Source>FromBody</Source>
                    <SubParameters>
                        <Parameter>
                            <Name>fooId</Name>
                            <Source i:nil="true" />
                            <SubParameters i:nil="true" />
                            <Type>System.String</Type>
                        </Parameter>
                        <Parameter>
                            <Name>fooName</Name>
                            <Source i:nil="true" />
                            <SubParameters i:nil="true" />
                            <Type>System.String</Type>
                        </Parameter>
                    </SubParameters>
                    <Type>ApiTest.Controllers.Foo</Type>
                </Parameter>
            </Parameters>
            <SupportedHttpMethods>POST</SupportedHttpMethods>
        </ActionMethod>
    </ArrayOfActionMethod>
    

    More information on the ApiExplorer class can be found on MSDN.