Reputation: 557
I'm trying to get the Attribute tag from an API Controller so that I can see what HTTP verb it allows at runtime. From the sample code below, I want to be able to get the [HttpGet]
tag.
[HttpGet]
public void MyResource()
{
// Controller logic
}
I am currently using System.Reflection
to gather other information about my API while it is running, but so far I have been unable to retrieve the [HttpGet
tag and other Http
verb tags. I've tried each of the solutions below with no luck:
public void GetControllerMethodHttpAttribute()
{
MethodInfo controllerMethod = typeof(TestController).GetMethods().First();
// Solution 1
var attrs = controllerMethod.Attributes;
// Solution 2
var httpAttr = Attribute.GetCustomAttributes(typeof(HttpGetAttribute));
// Solution 3
var httpAttr2 = Attribute.GetCustomAttribute(controllerMethod, typeof(HttpGetAttribute));
// Solution 4
var httpAttr3 = Attribute.IsDefined(controllerMethod, typeof(HttpGetAttribute));
}
All of the previous questions that I've researched about this topic only related to Custom Attribute tags
and pulling values out of those, but I couldn't find any information about getting the framework-included Attribute tags
.
Does anyone know how I can get the [HttpGet]
Attribute tag?
Thanks!
Upvotes: 6
Views: 4681
Reputation: 28509
The proposed solutions 3 and 4 work.
You must look out that you are referencing the correct HttpGetAttribute
. There is one in System.Web.Http.HttpGetAttribute
and there is one in System.Web.Mvc.HttpGetAttribute
.
The following code lists the public methods of the ValuesController
API controller with the information on whether they have HttpGet or HttpPost attributes.
var methods = typeof(ValuesController).GetMethods();
string infoString = "";
foreach(var method in methods)
{
// Only public methods that are not constructors
if(!method.IsConstructor && method.IsPublic)
{
// Don't include inherited methods
if(method.DeclaringType == typeof(ValuesController))
{
infoString += method.Name;
if(Attribute.IsDefined(method, typeof(System.Web.Http.HttpGetAttribute)))
{
infoString += " GET ";
}
if(Attribute.IsDefined(method, typeof(System.Web.Http.HttpPostAttribute)))
{
infoString += " POST ";
}
infoString += Environment.NewLine;
}
}
}
You need to exchange System.Web.Http.
with System.Web.Mvc.
when the controller is an MVC controller instead of an API controller.
Upvotes: 8
Reputation: 17579
This is how I ended up doing it:
public static IEnumerable<Attribute> GetSupportedVerbsForAction<T>(
Expression<Func<T, IActionResult>> expression)
where T : Controller
{
//only consider a list of attributes
var typesToCheck = new[] { typeof(HttpGetAttribute), typeof(HttpPostAttribute),
typeof(HttpPutAttribute), typeof(HttpDeleteAttribute), typeof(HttpPatchAttribute)};
var method = ((MethodCallExpression)expression.Body).Method;
var matchingAttributes = typesToCheck
.Where(x => method.IsDefined(x))
.ToList();
//if the method doesn't have any of the attributes we're looking for,
//assume that it does all verbs
if (!matchingAttributes.Any())
foreach(var verb in typesToCheck)
yield return verb;
//else, return all the attributes we did find
foreach (var foundAttr in matchingAttributes)
{
yield return method.GetCustomAttribute(foundAttr);
}
}
Say you have the following Controller and Actions:
public class HomeController : Controller
{
// implicit get
public IActionResult Index() => View();
// explicit get
[HttpGet]
public IActionResult GetAction() => View();
// extra info
[HttpPost(Name = "some name", Order = 5)]
public IActionResult PostAction() => View();
[HttpPut]
[HttpDelete]
[HttpPatch("my template", Name = "patch name", Order = 333)]
public IActionResult MultiAction() => View();
}
You can call them like this:
var indexVerbs = GetSupportedVerbsForAction<HomeController>(x => x.Index()).ToList();
var getVerbs = GetSupportedVerbsForAction<HomeController>(x => x.GetAction()).ToList();
var postVerbs = GetSupportedVerbsForAction<HomeController>(x => x.PostAction()).ToList();
var multiVerbs = GetSupportedVerbsForAction<HomeController>(x => x.MultiAction()).ToList();
This will return the full attribute that you've set in case you've added any custom data.
Two things to note:
GetSupportedVerbsForAction<HomeController>(x => x.Index)
.Attribute
, so use the is
keyword to figure out if it's the type you want.Upvotes: 2