asp.net swashbucle/swagger not showing post - asp.net-web-api

I have configured swashbucle in my asp.net project and is working ok but one problem. It does not show the info for one method i.e. POST.
[ResponseType(typeof(CRUD_Request_Response))]
[HttpPost]
public CRUD_Request_Response Post(CRUD_Request_Response _theCRUD)
{
return ProcessCRUD(theCRUD);
}
[ResponseType(typeof(CRUD_Request_Response))]
[HttpPut]
public CRUD_Request_Response Put(CRUD_Request_Response _theCRUD)
{
return ProcessCRUD(theCRUD);
}
Any suggestions ?

If 2 methods have the same signature. Swashbuckle gets confused about the difference between the two and does not show eather.

Related

500 Error when setting up Swagger in asp .net CORE / MVC 6 app

I'm trying to setup a basic swagger API doc in a new asp .net CORE / MVC 6 project and receiving a 500 error from the swagger UI:
500 : http://localhost:4405/swagger/v1/swagger.json
My startup class has the following code in it:
using Swashbuckle.SwaggerGen;
using Swashbuckle.SwaggerGen.XmlComments;
using Swashbuckle.Application;
....
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSwaggerGen();
services.ConfigureSwaggerDocument(options =>
{
options.SingleApiVersion(new Info
{
Version = "v1",
Title = "Blog Test Api",
Description = "A test API for this blogpost"
});
});
}
and then under Configure:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
....
app.UseSwaggerGen();
app.UseSwaggerUi();
....
}
When i build and run the project, the UI will come up when i go to swagger/UI/index.html, but the 500 error above is displayed. When i go to the swagger/v1/swagger.json link, console gives the following 500 error:
Failed to load resource: the server responded with a status of 500 (Internal Server Error)
Is there any way i can figure out the root cause of the 500 or enable any additional debug in swagger to figure out why it's throwing this error? Based on some of the tutorials i've looked at, only what i have in startup is required for a base implementation. Please let me know if i can provide any additional information.
EDIT: this is for rc1, and may not be relevant to the new netcore 1.0 currently out (6/29/2016)
If someone want to know the exact error is in the Swagger's stack trace, request the URL:
<your-app-url>/swagger/v1/swagger.json
Or, click on the swagger.json link from the browser dev tools console:
Which will show the error in your IDE Output:
Initially I got a 500 error too. Deep down in the stacktrace it said:
System.NotSupportedException: Unbounded HTTP verbs for path 'api/hotels'. Are you missing an HttpMethodAttribute?
It turned out I was missing a HttpGet attribute for one of my api methods:
[Microsoft.AspNetCore.Mvc.HttpGet]
also if you used a method with a parameter like this "Get(int id)"
you will get the same error without an explanation so you need to add it into the decoration
"[HttpGet("{id:int}")]"
I got this error when one of my functions was marked as public, but wasn't meant to be a web service which could be called directly.
Changing the function to private made the error go away.
Alternatively, immediately before your public function, you can put the [NonAction] command, to tell Swagger to ignore it.
[NonAction]
public async Task<IActionResult> SomeEvent(string id)
{
...
}
(I wish Swagger would actually report the name of the function which caused this problem though, rather than just complaining that it could no longer find the "../swagger/v1/swagger.json" file... that's not particularly useful.)
Firstly you can enable the developer exception page by adding app.UseDeveloperExceptionPage(); on your Configure() in order to see better which is the root cause. Take a look here
In my case the problem was that I have to install also Microsoft.AspNetCore.StaticFiles nuget in order to make Swagger work.
Try also to uninstall/reinstall Swashbuckle.AspNetCore nuget.
I had this problem today and the cause was that some methods on my controllers API was missing [HttpGet]:
The exception (in stack trace) showed me the problme
You can also check the exception in the Output window in Visual Studio like this (in my case it showed me):
Look here if you're not able to load the and look at the swagger.json in the console.
Swagger has a difficult time negotiating the differences between namespaces. When building the objects expected for api calls it will index through each defined class. If there are two classes that share a class name it won't be able to process the swagger.json file.
Example of two classes that .Net will process correctly, but Swagger will not.
namespace MyCompany.PaymentProcessor.DTO
{
public class Payment
{
//dto content
}
}
and
namespace MyCompany.CbData
{
public class Payment
{
//couch base data
}
}
Will be treated correctly by .Net, but unresolvable by swagger.
In my case I was missing an action in route attribute which exist in your API controller.
Something like this:
[Route("api/[controller]/[action]")]
Before I had:
[Route("api/[controller]")]
An error occoures when writing [Route("api/[controller]")] because swagger doesn't know how to separate the API methods without action inside your route attribute.
Had the same problem and the error message helped me identify the root cause:
{
"error": "Conflicting method/path combination \"POST api/calls\" for actions - SMSApi_v2.Controllers.CallController.CreateCall (SMSApi_v2),SMSApi_v2.Controllers.CallController.CreateCalls (SMSApi_v2). Actions require a unique method/path combination for Swagger/OpenAPI 3.0. Use ConflictingActionsResolver as a workaround"
}
The root were these lines of code:
**[HttpPost("calls")]**
public IActionResult CreateCall([FromBody]Call call)
{
repository.Create(call);
return Ok(call);
}
**[HttpPost("calls")]**
public IActionResult CreateCalls([FromBody] string xmlFile)
{
var calls = xmlProcessor.DeserializeTo<List<Call>>(xmlFile);
if (!calls.Any())
return BadRequest("Deserializing was not done correctly.");
repository.Create(calls);
return Ok(calls);
}
Even if the signatures of the methods are different, the two API verbs have the same route and this is generating the error.
Add [HttpGet] or [HttpPost] on top of api actions.
Add [Reout("YourApiActionName")] on top of api actions ,
or add [Route("[controller]/[action]")] on top of your Controller class.
Also if I may add, the swagger set up does not like it when you route at the root level of your controllers. For example:
Do not do this:
[Produces("application/json")]
[Route("/v1/myController")]
[Authorize]
public class myController
{
[SwaggerResponse((int)System.Net.HttpStatusCode.OK, Type = typeof(RestOkResponse<Response>))]
[SwaggerResponse((int)System.Net.HttpStatusCode.InternalServerError, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.BadRequest, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.Forbidden, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.NotFound)]
[HttpPost]
[Authorize()]
public async Task<IActionResult> Create([FromBody] MyObject myObject)
{
return Ok();
}
}
Do this:
[Produces("application/json")]
[Authorize]
public class myController
{
[SwaggerResponse((int)System.Net.HttpStatusCode.OK, Type = typeof(RestOkResponse<Response>))]
[SwaggerResponse((int)System.Net.HttpStatusCode.InternalServerError, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.BadRequest, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.Forbidden, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.NotFound)]
[HttpPost("/v1/myController")]
[Authorize()]
public async Task<IActionResult> Create([FromBody] MyObject myObject)
{
return Ok();
}
}
It took me a while to figure that the reason why I was getting internal server error was because of this routing issue. Hope this helps someone!
Might be obvious but, besides missing the HttpGet or HttpPost attributes, don't forget to differentiate the post methods.
You may have 2 different methods (with different names) marked with HttpPost, and that would also cause this kind of issue. Remember to specify the method name in the attribute: [HttpPost("update")].
I get same error in ASP.NET Boilerplate. I searched a lot and found a problem with my code. I use same name two DTO object, but located different namespaces.
For example first DTO object is like as below:
namespaces Test{
public class TestDto
{
public int Id{get;set;}
}
}
And second DTO object is like as below:
namespaces Test_2{
public class TestDto
{
public int Id{get;set;}
}
}
I changed Test_2.TestDto's name, problem did solve for me after.
In my case, a model has the same name as another model, I fixed changing the name
When I Add the parameter Version , it works
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
});
Also had this problem. In my case, it was caused by two endpoints in the same controller with the same route and method name (but different parameter types). Of course, it then became apparent that that was probably poor practice anyway so I changed the endpoint names and all was well.
in some cases, the router of controller is duplicated. Review the last controller modified.
I was getting this error because in STARTUP.CS I not put the version's name in SwaggerDoc parameters:
Error => c.SwaggerDoc("", blablabla
WORK => c.SwaggerDoc("v1",blablabla
then, now are ok fine!
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info {Title = "PME SERVICES", Version = "v1"});
});
I ran into this issue today configuring Swagger in a .Net Core 2.2 Web Api project. I started down the path that #Popa Andrei mentions above by including the Microsoft.AspNetCore.StaticFiles dependency in my project as I figured that was most likely the culprit. That turned into a rabbit hole of chaining dependencies although it did ultimately work for me.
I then realized that in my ConfigureServices method in Startup I had services.AddMvcCore(...) which just gives you bare bones and you add dependencies as you need them. When I changed that to services.AddMvc(...) it started working without having to manually add all the dependencies required by Microsoft.AspNetCore.StaticFiles.
That doesn't mean you can't take the route of staying with services.AddMvcCore(...) and then adding all the necessary dependencies. You can, and it will work.
It is just much easier to take the services.AddMvc(...) approach and be done.
Hope that helps someone.
Making sure my swagger versions lined up with each other fixed my issue. Since I was starting a new project I set my api version to be v0.1
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v0.1", new Info { Title = "Tinroll API", Version = "v0.1" });
});
But had left my swagger url to be v1.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Tinroll API v0.1");
c.RoutePrefix = string.Empty;
});
I updated my versioning to be /swagger/v0.1/swagger.json instead of v1 and Swagger worked as expected.
Since I don't see the solution which worked for me posted here, I will contribute one to the ongoing thread. In my case, it was the Route attribute was set separately with the HttpPost/HttpGet at the function level (not controller level).
INCORRECT:
[HttpPost]
[Route("RequestItem/{itemId}")]
CORRECT:
[HttpPost("RequestItem/{itemId}")]
Also, the Swagger seems to expect Ok(object) result instead of StatusCode(object) result for a success request to return.
For me it was because of having two class types with the same name but with different namespaces, which are used as the return type of two different actions in different controllers!
When I changed the name of one of them, the problem solved!
For me the problem was due to OData. If I just commented out my services.AddOData(); I didn't get any error.just comment out the services.AddOData();
If you use Swagger, which is enabled by default in .Net Core 5, it needs to know something about your methods. Normally, you don't need to add [HttpGet] attribute because it is the default HttpMethod for your methods, but swagger requires that information to generate documentation of your code.
So adding [HttpGet] above my method solved my issue.
Might you've missed adding API verb to an endpoint. Can use below header as your need
1.[Microsoft.AspNetCore.Mvc.HttpGet]
2.[Microsoft.AspNetCore.Mvc.HttpPost]
This came because you have a no-action method on your controller class check that missed an HTTP attribute on any of the controller action methods. If you need a no-action or no need for access from external methods declaration then make it private, you will fix this issue.
private void MyMethod()
{
}
Give a look at this project.
https://github.com/domaindrivendev/Ahoy/tree/master/test/WebSites/Basic
This repo is from Swashbuckle´s owner, is a basic ASP.NET 5 Sample app, this is help you to correct configure yours middlewares (and take care about the orders of them, it´s matter, e.g., use "app.UseSwaggerGen();app.UseSwaggerUi(); after app.UseMvc();)
To enable logging in your applcation give a look at:
https://docs.asp.net/en/latest/fundamentals/logging.html?highlight=logging
(the log will be generated inside "wwwroot" folder
To see the source of exception
open chrome browser
open developer tools
see exceptions in console tab
fix it.
The setup for Swagger is varying greatly from version to version. This answer is for Swashbuckle 6.0.0-beta9 and Asp.Net Core 1.0. Inside of the ConfigureServices method of Startup.cs, you need to add -
services.AddSwaggerGen(c =>
{
c.SingleApiVersion(new Info
{
Version = "v1",
Title = "My Awesome Api",
Description = "A sample API for prototyping.",
TermsOfService = "Some terms ..."
});
});
Then in the Configure method you must add -
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMvc();
app.UseSwaggerGen();
app.UseSwaggerUi();
}
Be sure you are referencing in Startup.cs -
using Swashbuckle.SwaggerGen.Generator;
My project.json file looks like -
"dependencies": {
"Microsoft.AspNetCore.Mvc": "1.0.0-rc2-final",
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0-rc2-final",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0-rc2-final",
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0-rc2-final",
"Microsoft.EntityFrameworkCore.SqlServer.Design": "1.0.0-rc2-final",
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-*",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-rc2-final",
"Microsoft.Extensions.Configuration.FileExtensions": "1.0.0-rc2-final",
"Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-final",
"Microsoft.Extensions.Logging": "1.0.0-rc2-final",
"Microsoft.Extensions.Logging.Console": "1.0.0-rc2-final",
"Microsoft.Extensions.Logging.Debug": "1.0.0-rc2-final",
"Swashbuckle": "6.0.0-beta9"
},
"tools": {
"Microsoft.AspNetCore.Server.IISIntegration.Tools": {
"version": "1.0.0-preview1-final",
"imports": "portable-net45+win8+dnxcore50"
},
"Microsoft.EntityFrameworkCore.Tools": {
"version": "1.0.0-preview1-final",
"imports": [
"portable-net45+win8+dnxcore50",
"portable-net45+win8"
]
}
},
"frameworks": {
"net452": { }
},
"buildOptions": {
"emitEntryPoint": true,
"preserveCompilationContext": true,
"xmlDoc": false
},
"publishOptions": {
"include": [
"wwwroot",
"Views",
"appsettings.json",
"web.config"
]
},
"scripts": {
"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
}
}

Restricting auto Help Page contents when using Attribute Routing in Web API 2

I'm currently implementing a Web API using Web API 2's attribute routing (http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2). I am also using the Help Pages module in order to automatically generate documentation from XML comments (http://www.asp.net/web-api/overview/creating-web-apis/creating-api-help-pages).
For this API I am providing support for optional return format extensions, so that every API method has a pair of routes defined on it like so:
[HttpGet]
[Route("Path/Foo")]
[Route("Path/Foo.{ext}")]
public HttpResponseMessage DoFoo()
{
// Some API function.
}
This allows a user to hit any of these and get a result:
www.example.com/api/Controller/Path/Foo
www.example.com/api/Controller/Path/Foo.json
www.example.com/api/Controller/Path/Foo.xml
My issue is that when Help Pages uses MapHttpAttributeRoutes() to generate documentation, it is picking up both routes for each method. So right now I see help for:
api/Controller/Foo
api/Controller/Foo.{ext}
But I want to only see:
api/Controller/Foo.{ext}
I would prefer to hide the non-extension route on each method, so that every method only shows a single Help Page entry.
Has anyone else tried something similar? Is there a work around that I am missing?
My question would be is that, would consumers of your api figure out easily that the {ext} is optional?...personally, I would prefer the default behavior...but anyways following are some workarounds that I can think of:
A quick and dirty workaround. Split the DoFoo into 2 actions like DoFoo() and DoFooWithExt maybe. Notice that I am using an attribute called ApiExplorerSettings, which is for HelpPage purposes. Example below:
[HttpGet]
[Route("Path/Foo")]
[ApiExplorerSettings(IgnoreApi=true)]
public HttpResponseMessage DoFoo()
{
return DoFooHelper();
}
[HttpGet]
[Route("Path/Foo.{ext}")]
public HttpResponseMessage DoFooWithExt()
{
return DoFooHelper();
}
private HttpResponseMessage DoFooHelper()
{
//do something
}
Create a custom ApiExplorer (which HelpPage feature uses internally) and check for specific routes like the following and can decide whether to show the action or not for that particular route.
// update the config with this custom implementation
config.Services.Replace(typeof(IApiExplorer), new CustomApiExplorer(config));
public class CustomApiExplorer : ApiExplorer
{
public CustomApiExplorer(HttpConfiguration config) : base(config)
{
}
public override bool ShouldExploreAction(string actionVariableValue, HttpActionDescriptor actionDescriptor, IHttpRoute route)
{
if (route.RouteTemplate.EndsWith("Path/Foo", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return base.ShouldExploreAction(actionVariableValue, actionDescriptor, route);
}
}
Get list of all ApiDescription from the default ApiExplorer and then filter out the descriptions which you do not like. Example:
Configuration.Services.GetApiExplorer().ApiDescriptions.Where((apiDesc) => !apiDesc.RelativePath.EndsWith("Path/Foo", StringComparison.OrdinalIgnoreCase))

405 when using AttributeRouting.PUTAttribute unless I also include HttpPutAttribute

We have an MVC project that I am attempting to update to include WebApi. In order to get the required routes we are using AttributeRouting. All the calls seem to be routing correctly except for [PUT] which returns a 405. I have simplified the controller and actions and still receive the error with the [PUT] unless I include [HttpPut] also. Not sure what I am missing.
[RoutePrefix("api/Sites")]
public class SitesController : BaseApiController
{
[POST("")]
public bool CreateSite(SiteSignupArgs args)
{
...
}
[GET("Statuses")]
public IList<SiteAuditViewModel> GetStatuses()
{
...
}
[PUT("Statuses/{siteId}")]
[HttpPut] // This is required or 405 is returned
public HttpResponseMessage UpdateStatus(string siteId, UpdateStatusArgs args)
{
...
}
[DELETE("Statuses/{siteId}")]
public HttpResponseMessage Delete(string siteId)
{
return Request.CreateResponse(HttpStatusCode.OK);
}
}
Version 3.5.6 of AttributeRouting.Core, AttributeRouting.Core.Http, AttributeRouting.Core.Web, AttributeRouting.WebApi
MVC4
WebDAV is not installed.
What you are seeing is an expected behavior. Action Selector in Web API by default assumes the action to be of verb POST if the action name does not have a prefix with verbs like "Get", "Post", "Put", "Delete" etc.
Now it isn't working even if you have specified [PUT("Statuses/{siteId}")] attribute explicitly because, Action selector looks for attributes from System.Web.Http namespace like HttpGetAttribute, HttpPostAttribute, HttpPutAttribute etc.
Since AttributeRouting's PUTAttribute isn't of the above types, Action selector doesn't consider it and still thinks it to be the default one, which is POST. So your workaround of having HttpPut attribute is correct.

Add Aspects to ASP.NET MVC controller using AspectMap

We're looking at using an AOP framework for handling things like logging, tracing, and exception handling. I've built a prototype using PostSharp and now I'm trying to build the same functionality using AspectMap.
In a nutshell, I have an ASP.NET MVC 3 application and I want an aspect that I can easily attach to my controller methods that shows the entry, exit, execution time, and argument values. My PoC is the basic MVC 3 Internet Application template (File > New > Project > Web > ASP.NET MVC 3 Web Application > Internet). What I've done so far...
Created an AspectsRegistry
public class PoCRegistry : AspectsRegistry
{
public PoCRegistry()
{
ForAspect<ProfileAttribute>().HandleWith<ProfileHandler>();
}
}
Created a StructureMapControllerFactory
public class StuctureMapControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance( RequestContext requestContext, Type controllerType )
{
if( controllerType == null ) return null;
try
{
return ObjectFactory.GetInstance( controllerType ) as Controller;
}
catch( StructureMapException )
{
Debug.WriteLine( ObjectFactory.WhatDoIHave() );
throw;
}
}
}
Registered everything in Application_Start
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters( GlobalFilters.Filters );
RegisterRoutes( RouteTable.Routes );
ObjectFactory.Initialize( ie => ie.AddRegistry( new PoCRegistry() ) );
ControllerBuilder.Current.SetControllerFactory( new StuctureMapControllerFactory() );
}
At this point the application works, and I can see it's using my StructureMapControllerFactory to build the controller (debugger steps into that code). The problem is that I can't figure out where or how to "enrich" the controller that is generated. In the tutorial it says I need to use something like the following:
For<ICaseController>()
.Use<CaseController>()
.EnrichWith( AddAspectsTo<CaseController> );
But in the tutorial that goes in the AspectRegistry, which doesn't seem like the right place in this situation because the registry isn't responsible for resolving the controller request, the controller factory is. Unfortunately the GetInstance() method in the controller factory returns an object and the EnrichWith() method needs a SmartInstance.
At this point I'm stuck. Any hints, pointers, or assistance would be appreciated.
This is a use case I hadn't thought about to be honest. I'll setup a test project today and see what I can come up with. Bear with me!
Update
I've been playing around with the backend code (you can get a complete copy of the code from http://aspectmap.codeplex.com) and the relevant part is this:
public T AddAspectsTo<T>(T concreteObject)
{
ProxyGenerator dynamicProxy = new ProxyGenerator();
return (T)dynamicProxy.CreateInterfaceProxyWithTargetInterface(typeof(T), concreteObject,
new[] { (IInterceptor)new AspectInterceptor(attributeMap) });
}
This is using the castle dynamic proxy stuff. Unfortunately the CreateInterfaceProxy... methods require that an interface is passed in (rather than a base class like I'd hoped). Now I've found this question:
C# Dynamic Proxy 2 generate proxy from class with code in constructor ? How to?
That seems to show that it could be possible to use CreateClassProxy. I've not had chance to try this out yet and I'm going away for a week away from the computer. If you want to try and wire it up though you're welcome to get the source from codeplex and give it a try though. If not I'll put something together when I return.
Action filters can be used to provide such AOP functionality.
http://www.asp.net/mvc/tutorials/older-versions/controllers-and-routing/understanding-action-filters-cs
http://msdn.microsoft.com/en-us/library/dd410056%28v=vs.90%29.aspx

Problem with Ninject and MVC3 Dependency injection action filter on Controller and Action

Recently I decided to remove a heap of action level filters in a controller and replace them with a single controller level filter.
Now I'm getting this error message.
Error activating LogActionFilter
More than one matching bindings are available.
Activation path:
1) Request for LogActionFilter
Suggestions:
1) Ensure that you have defined a binding for LogActionFilter only once.
I'm sure the error is related to action filter being bound twice, as that's what I've changed. However, when I view the documentation here I can see it specifies/does the same. So I'm really not sure what I'm doing wrong.
My sample controller
[LogAction]
public class SomeController : Controller
{
public ActionResult SomeAction()
{
}
}
My registration code
public static void RegisterFilters()
{
Kernel.BindFilter<LogActionFilter>(FilterScope.Controller, 0)
.WhenControllerHas<LogActionAttribute>();
Kernel.BindFilter<LogActionFilter>(FilterScope.Action, 0)
.WhenActionMethodHas<LogActionAttribute>();
}
This happens if your controller and one of its actions have the LogActionAttribute at the same time.
(I know the answer's already accepted but this is for the sake of documentation.)
In case you can only use the release version, a temporary solution is to create two subclasses and register them separately. Here's an example from my application:
public class MyAuthorizationFilter : IAuthorizationFilter
{
/* call base ctor */
}
public class MyControllerAuthorizationFilter : MyAuthorizationFilter
{
/* call base ctor */
}
public class MyActionAuthorizationFilter : MyAuthorizationFilter
{
}
Then setup filter bindings:
this.BindFilter<MyControllerAuthorizationFilter>(FilterScope.Controller, 0)
.WhenControllerHas<MyAttribute>()
.WithConstructorArgumentFromControllerAttribute<ProtectedAttribute>(/*...*/) ;
this.BindFilter<MyActionAuthorizationFilter>(FilterScope.Action, 0)
.WhenActionMethodHas<MyAttribute>()
.WithConstructorArgumentFromActionAttribute<ProtectedAttribute>(/*...*/) ;
Make sure to call the correct 'WithConstructorArgumentFrom[Controller/Action]Attribute method or you'll get a 'Sequence has no elements' error (I did).
Better workaround. In fact I use this in the new version too rather than have two bindings for controllers and actions.
this.BindFilter<MyFilter>(FilterScope.Global, int.MinValue)
.When((controllerContext, actionDescriptor) =>
controllerContext
.Controller
.GetType()
.GetCustomAttributes(typeof(MyAttribute),true)
.Length > 0
|| actionDescriptor.GetCustomAttributes(typeof(MyAttribute), true).Length > 0);

Resources