6

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.

Melih Mucuk
  • 6,988
  • 6
  • 37
  • 56
  • I recently built a web-api solution and needed to generate access to the classes and requests/response myself. Using WebAPI Odata allowed access to these objects via the meta data link. Here is a link to one of the tutorials I followed. http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v3/creating-an-odata-endpoint The odata provider has a metadata resource that other applications can use to get a strongly typed xml schema of your access points that can then be turned into objects by said applications for integrating with your WebAPI Odata end point. – Pynt Aug 13 '14 at 16:59
  • @Pynt thanks for comment. That was very helpful, but this is just gives models. I need controllers and models. For example there are 5 controllers and 5 models. I need which controller uses Model1, which controller uses Model2. Also I need controllers url and HTTP Method Type (GET,POST etc). – Melih Mucuk Aug 14 '14 at 16:13
  • When you say it only gives models, are you referring to the results of the service reference after you've setup the WebAPI-OData controllers when consumed by an outside application? If so those models should map 1 to 1 with your controllers on the WebAPI, it is upto the secondary application to be able to properly use those 'models' to build the OData queries to retrieve data. AngularJS has an OData framework. As well as C# of course, and they can both build the queries from the WebAPI service reference. – Pynt Aug 14 '14 at 18:49
  • @Pynt is there any sample about it ? – Melih Mucuk Aug 14 '14 at 20:13
  • The link provided in my initial comment has the source code available at the very top of the page, but I'll include it here as well: http://code.msdn.microsoft.com/ASPNET-Web-API-OData-cecdb524 . Key points to examine would be the model, the controller that visual studio created from the model, the webapiconfig.cs which contains the route data. Or do you want a sample of consuming the WebAPI Odata service from another project? – Pynt Aug 14 '14 at 20:22
  • @Pynt Yes, for example I need sample of console application that consumes Web API OData services. – Melih Mucuk Aug 15 '14 at 08:36
  • Is this for documenting all the API or create a proxy class for the mobile – satish Aug 15 '14 at 14:13
  • This is for generating client codes. For iOS and Android. – Melih Mucuk Aug 15 '14 at 14:49
  • outside of writing you the code, my answer gives you everything you need to do this. – T McKeown Aug 17 '14 at 15:23

2 Answers2

9

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.

petelids
  • 12,305
  • 3
  • 47
  • 57
4

To get the assembly that contains your controllers, add an action to one of these controllers so when executed you can Get the assembly:

var types = GetType().Assembly.GetTypes();

Now you can loop through these types and test to see what type it is. If all your controllers implement a common interface or extend a common class etc... You can also test the namespace..

For example:

for (var t in types)
{
   if (t.IsSubClassOf(typeof( ApiController))){  .... }
}

Here is a tutorial/example:

How to get all types in the references that implement IMyInterface

Community
  • 1
  • 1
T McKeown
  • 12,971
  • 1
  • 25
  • 32