问题
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.
回答1:
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.
回答2:
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
来源:https://stackoverflow.com/questions/25206833/list-of-controllers-and-classes-in-asp-net-web-api