How to show endpoint documentation when my method inherits a base controller - .net-core

I have multiple classes (more than 100) which inherit from my base class BaseController. All my classes are their logics and models but format's response (200, 404, 500, ...) are always the same.
But when I inherit from my base class, in my swagger documentation I see my endpoints but the details of the response are not there. How can I do this?
public class BaseController : Controller
{
public BaseController() {}
[Produces("application/json")]
[SwaggerResponse(StatusCodes.Status200OK)]
[SwaggerResponse(StatusCodes.Status404NotFound)]
protected async Task<IActionResult> Get(int id)
{
...
}
}
public class MyController : BaseController
{
[HttpGet("{id}")]
public async Task<IActionResult> Get(int id)
{
return await base.Get(id).ConfigureAwait(false);
}
}

As #Helder Sepulveda Said, this goes beyond swashbuckle.
I Think you can use IActionModelConvention to simulate inherit the action attributes.
Use Action.Filters like this
public class ActionMethodConvention : IActionModelConvention
{
public void Apply(ActionModel action)
{
var actonBaseResponses = new List<SwaggerResponseAttribute>();//some code to get baseAction reflections
foreach (var attr in actonBaseResponses)
{
action.Filters.Add(new Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute(actonBaseResponses.StatusCode));
}
}

Related

Return correct 201 when using controller actions attribute routing

Net core 3 web api. It uses attribute routing for controllers actions. Few separate controllers with post methods (e.g. CreatedContract) should return 201 where routeName related to : GetOperationsById. Example is very simplified comparing to actual app, so please lets not put to much attention to it style
How to set route name to CreatedAtRoute correctly:
[ApiController]
public class OperationsController : ControllerBase
{
[HttpGet]
[Route("contractsOperation/{id}")]
public async Task<IActionResult> GetOperationsById(int id)
{
var operation = _service.GetOperation(id);
return Ok(operation);
}
}
[ApiController]
public class ContractsController : ControllerBase
{
[HttpPost]
[Route("saveContract")]
public async Task<IActionResult> CreatedContract(string jsonData)
{
var newlyCreatedContract = _service.Create(jsonData);
//var route = this value should point out to OperationsController -> GetOperationsById
return CreatedAtRoute(route, new { newlyCreatedContract.Id }, newlyCreatedContract);
}
}
Thank you
According to your description, I suggest you could try to add a route name to the GetOperationsById route attribute and then use this attribute name as the parameter to the CreatedAtRoute method.
More details, you could refer to below example:
Other controller method:
[HttpGet]
[Route("contractsOperation/{id}", Name = nameof(GetOperationsById))]
public async Task<IActionResult> GetOperationsById(int id)
{
return Ok("aa");
}
Call it:
[HttpGet]
public IActionResult Index()
{
return CreatedAtRoute(nameof(FooController.GetOperationsById), new { id = 1 }, new { id=1});
}
Result:

Same Policy but different required parameter for each action method

In a .Net core Webapi 2.1 project, I have a tons of action methods.
All action methods should be authorized against the same policy (named FooPolicy) but with a different required argument.
Based on Microsoft's docs: Policy-based-Authorization
One way would be to declare a tons of policies based on different input argument:
services.AddAuthorization(options =>
{
options.AddPolicy("FooPolicy1", policy =>policy.Requirements.Add(new FooRequirement(1)));
options.AddPolicy("FooPolicy2", policy =>policy.Requirements.Add(new FooRequirement(2)));
options.AddPolicy("FooPolicy3", policy =>policy.Requirements.Add(new FooRequirement(3)));
//... May be 30 more same policies here ...
});
As i earlier mentioned, only different part is in new FooRequirement(diffArgs). The other challenge for this solution would be to add each FooPolicy on it's corresponding action method and you may miss a couple of theme:
[Authorize(Policy = "FooPolicy1")]
public IActionResult ActionMethodFoo1(...) {...}
[Authorize(Policy = "FooPolicy2")]
public IActionResult ActionMethodFoo2(...) {...}
[Authorize(Policy = "FooPolicy3")]
public IActionResult ActionMethodFoo3(...) {...}
...List still goes on...
Is there any solution like: declare a policy once but use it with different instance of FooRequirement (which is of type IAuthorizationHandler)? like so:
services.AddAuthorization(options =>
{
options.AddPolicy("FooPolicy", policy =>policy.Requirements.Add(?));
});
And on the action methods:
[Authorize(Policy = "FooPolicy", required = new FooRequirement(1))]
public IActionResult ActionMethodFoo1(...) {...}
[Authorize(Policy = "FooPolicy", required = new FooRequirement(2))]
public IActionResult ActionMethodFoo2(...) {...}
[Authorize(Policy = "FooPolicy", required = new FooRequirement(3))]
public IActionResult ActionMethodFoo3(...) {...}
The main idea is to declare policy once. Two recent code blocks are psudo-code, Does any body knows practical solution with similar concept?
You could implement your own IAuthorizationFilter
custom IAuthorizationFilter
public class CustomAuthorize : IAuthorizationFilter
{
private readonly int _input;
public CustomAuthorize(int input)
{
_input = input;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
//custom validation rule
if (_input == 1)
{
context.Result = new ForbidResult();
}
}
}
Custom CustomAuthorizeAttribute
public class CustomAuthorizeAttribute : TypeFilterAttribute
{
public CustomAuthorizeAttribute(int input) : base(typeof(CustomAuthorize))
{
Arguments = new object[] { input };
}
}
Use
[CustomAuthorizeAttribute(1)]
public IActionResult About()

Multiple URLs same action method

I need to share action methods between different controllers. Take for example the following 2 controllers:
public class AController : Controller
{
public ActionResult Index()
{
//print AController - Index
}
public ActionResult Test()
{
//print test
}
}
public class BController : Controller
{
public ActionResult Index()
{
//print BController - Index
}
}
Both controllers have an Index method which is different. The Test method however can be called from both controllers. So I want that when the following urls are entered the Test() method will execute:
AController/Test
BController/Test
I would appreciate any suggestions on how to achieve this.
Assuming the implementation of the Test() action is the same for both controllers, refactor it into a common service:
public interface ITestService {
string Test();
}
public TestService: ITestService {
public string Test() {
// common implementation
return "The test result";
}
}
Then set up Dependency Injection to acquire this service.
Your controllers then can use the common service.
public class AController : Controller {
private readonly ITestService _testService;
public AController(ITestService testservice) {
_testService = testservice;
}
public ActionResult Test() {
var vm = new TestViewModel();
vm.TestResult = _testService.Test();
return View("Test", vm);
}
}
public class BController : Controller {
private readonly ITestService _testService;
public BController(ITestService testservice) {
_testService = testservice;
}
public ActionResult Test() {
var vm = new TestViewModel();
vm.TestResult = _testService.Test();
return View("Test", vm);
}
}
Because the View Test.cshtml is rendered by both controllers, it should be placed in the Views\Shared\ folder.
You can define your own routes as described here: https://learn.microsoft.com/aspnet/core/mvc/controllers/routing
So you can define as many routes as you like to point to the "Test" method inside "AController" just like this:
routes.MapRoute("Atest", "AController/Test",
defaults: new { controller = "AController", action = "Test" });
routes.MapRoute("Btest", "BController/Test",
defaults: new { controller = "AController", action = "Test" });
But you have to define them before the "default" route because otherwise the entered URL will match the default route conditions and so it will enter that route.
It´s also possible to define the route directly in top of the method.
public class AController : Controller
{
[Route("/Some/Route")]
public ActionResult Test()
{
}
}
I want to throw in an alternative solution. Create a base controller class to be inherited by the other two. Whatever you have there will be part of the children.
public class BaseController : Controller
{
public ActionResult Index()
{
//print AController - Index
}
// Add more methods to be shared between the other controllers
}
public class AController : BaseController
{
// Has Index method already from parent
// Unique method for A
public ActionResult Test()
{
//print test 1
}
}
public class BController : BaseController
{
// Has Index method already from parent
// Unique method for B
public ActionResult Test()
{
//print test 2
}
}
This implements the actual functionality in a single place. We use this method for many projects with no issues.

Inheritance of Action in AsyncController

I have a base AsyncController
BaseController : AsyncController
{
[Authorize("Admin")]
public virtual async Task<ActionResult> SomeMethod()
{
//code
}
}
How it is correct to inheritance and override SomeMethod?
UserController : BaseController
{
[Authorize("User")]
public override Task<ActionResult> SomeMethod()
{
return base.SomeMethod()
}
}
OR
UserController : BaseController
{
[Authorize("User")]
public override async Task<ActionResult> SomeMethod()
{
return await base.SomeMethod()
}
}
P.S. Sorry for my english
SomeMethod() is defined within your BaseController class. Therefore child classes should inherit from BaseController rather than AsyncController. To override the method, just add virtual keyword into the definition.
BaseController : AsyncController
{
[Authorize("Admin")]
public virtual async Task<ActionResult> SomeMethod()
{
//code
}
}
Inheritance and override
UserController : BaseController
{
[Authorize("User")]
public override Task<ActionResult> SomeMethod()
{
return base.SomeMethod()
}
}
Also I have noticed something odd in your Authorize attributes. If the base method is allow to Admin only and the child method is allow to User only, then you most likely end up being unable to execute the base method from the child class.

How to get roles array from Authorize Attribute of certain action in ASP.NET MVC?

Suppose I have following Controller and action with authorization Attribute:
public class IndexController : Controller
{
//
// GET: /Index/
[Authorize(Roles="Registered")]
public ActionResult Index()
{
return View();
}
}
I've searched over the entire Internet and not found an answer for this simple question: how to get the roles annotated to an especific Action/Controller? In this case: Index Action has: string[] = {"Registered"}
Finally I found the solution! Was more easy than I thought! ahahha I need extend a class from AuthorizeAttribute and use it in actions. The information I need is the attribute "Roles" of the inherited class:
public class CustomAuthorizationAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var roles = this.Roles;
base.OnAuthorization(filterContext);
}
}
And on Index Controller:
public class IndexController : Controller
{
//
// GET: /Index/
[CustomAuthorizationAttribute(Roles = "Registered")]
public ActionResult Index()
{
return View();
}
}

Resources