I have a generic WebApi controller base class that implements a set of standard routes. I am now trying to provide an example with SwaggerRequestExample. Is there a way to influence the parent class method's annotation from the child class? Or can this be done through configuration or code instead of annotation?
Example
public abstract class GenericController<T> : ControllerBase
{
[HttpPost]
public virtual async Task<IActionResult> Create([FromBody] T entity)
{
var result = await _service.GetFromDb()
//the result is depending on T; so each implementing child class
//needs to have it's own example
return Ok(result);
}
}
public class StudentController : GenericController<Student> { }
There is no place to put
[SwaggerResponseExample(HttpStatusCode.OK, typeof(StudentRespnseExample))]
This is potentially a solution that can work. However, it still doesnt generate the example. There is some other pieces I am missing in my config. Unfortunately, I dont have time to investigate right now. Will come back later again.
public class StudentController : GenericController<Student>
{
[HttpPost]
[SwaggerResponseExample(HttpStatusCode.OK, typeof(StudentResponseExample))]
public override async Task<IActionResult> Create([FromBody] Student entity)
{
return await base.Create(entity);
}
}
Related
In few places in legacy code (more than 100 controllers), we are running action from other controllers.
In .NET Framework it runs OK - ClaimsPrincipal in both controller's action have correct values, but in .NET Core, running SecondController.internalPut() from FirstController gives me NullReferenceException.
FirstController:
[EnableCors]
public class FirstController : BaseApiController
{
public FirstController(IContextFactory contextFactory) : base(contextFactory)
{
}
[HttpPut]
[HttpPost]
[Route("/api/firstcontroller")]
public IActionResult Put([FromBody] MyDTO data)
{
var token = Identity.Token; // <--- correct value
var secondController = new SecondController(ContextFactory);
secondController.internalPut(something); <--- NullReferenceException
return Ok();
}
}
SecondController:
[EnableCors]
public class SecondController : BaseApiController
{
public SecondController(IContextFactory contextFactory) : base(contextFactory)
{
}
[HttpPut]
[HttpPost]
public async Task<IActionResult> Put(Guid myGuid)
{
internalPut(something); // <-- OK
return Ok();
}
internal void internalPut(object something)
{
var token = Identity.Token; // <--- NullReferenceException when running from FirstController!!
}
}
And BaseApiController with TokenIdentity:
[ApiController]
[Route("/api/[controller]")]
[Route("/api/[controller]/[action]")]
public class BaseApiController : ControllerBase
{
protected readonly IMyContextFactory ContextFactory;
public BaseApiController(IMyContextFactory contextFactory)
{
ContextFactory = contextFactory;
}
public TokenIdentity Identity => User?.Identity as TokenIdentity;
}
public class TokenIdentity : GenericIdentity
{
public Guid Token { get; set; }
public string User { get; set; }
public TokenIdentity(Guid token) : base(token.ToString())
{
Token = token;
}
}
How is the easiest fix for this bug? I know that I can change BaseApiController implementation to get ClaimsPrincipal from IHttpContextAccessor, but this means that I need to update constructors for all > 100 controllers in code...
It is another way to always have ClaimsPrincipal when we are calling action from another controller?
What I recommend as the correct solution
I can't emphasise enough how much I recommend moving shared functionality into its own services, or perhaps look at using the Mediator Pattern (e.g. using the MediatR library) to decouple your controllers from their functionality a little. What I provide below is not a solution, but a band-aid.
What I recommend a QUICK FIX only
Why is this only a quick fix?: because this doesn't instantiate the correct action details and route parameters, so it could potentially cause you some hard-to-find bugs, weird behaviour, URLs maybe not generating correctly (if you use this), etc.
Why am I recommending it?: because I know that sometimes time is not on our side and that perhaps you need a quick fix to get this working while you work on a better solution.
Hacky quick fix
You could add the following method to your base controller class:
private TController CreateController<TController>() where TController: ControllerBase
{
var actionDescriptor = new ControllerActionDescriptor()
{
ControllerTypeInfo = typeof(TController).GetTypeInfo()
};
var controllerFactory = this.HttpContext.RequestServices.GetRequiredService<IControllerFactoryProvider>().CreateControllerFactory(actionDescriptor);
return controllerFactory(this.ControllerContext) as TController;
}
Then instead of var secondController = new SecondController(ContextFactory); you would write:
var secondController = CreateController<SecondController>();
I am trying to build an MVC service which calls 2 different APIs, an Amazon one and an Apple one. The code looks like this:
public abstract class ApiHttpCaller<T>
{
protected static HttpClient _client;
protected ApiHttpCaller()
{
_client = new HttpClient();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public abstract Task<T> RetrieveApiResultAsync(string searchValue);
}
This ApiHttpCaller is implemented by my 2 specifics AmazonApiCaller and AppleApiCaller, let's take only one of them into account:
public class AmazonApiCaller : ApiHttpCaller<AmazonResponseModel>
{
protected static IOptions<ApiUrls> _apiUrls;
public AmazonApiCaller(IOptions<ApiUrls> apiUrls)
{
_apiUrls = apiUrls;
}
public override async Task<AmazonResponseModel> RetrieveApiResultAsync(string searchValue)
{
..logic to call the api..
string responseBody = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<AmazonResponseModel>(responseBody);
}
}
as you can see, correct me if the architecture is wrong, there is an AmazonResponseModel used as generics here. As you can imagine AmazonApi and AppleApi return 2 different models. That's why my abstract parent class ApiHttpCaller uses a generics T that into the specifc AmazonApiCaller becomes an AmazonResponseModel. Such APIs are called from my controller.
[Route("api/[controller]")]
[ApiController]
public class ItemsController<T> : ControllerBase
{
private readonly IEnumerable<ApiHttpCaller<T>> _apiCallers;
[HttpPost]
public async Task<ActionResult> Post([FromBody] string value)
{
var amazonCaller = _apiCallers.First(x => x.GetType() == typeof(AmazonApiCaller));
var itemResult = await amazonCaller.RetrieveApiResultAsync(value);
..more logic to map the itemResult to a viewModel..
}
}
So, first question is: do you think it's correct to use the genercis T in the controller that then becomes a specifc type inside each api caller?
Second and more important: I don't know how to register in Startup.cs the ApiHttpCallers in such a way that they get injected properly in my controller. First guess is:
services.AddSingleton<ApiCaller<T>, AmazonApiCaller<AmazonResponseModel>>();
services.AddSingleton<ApiCaller<T>, AppleApiCaller<AppleResponseModel>>();
point is Startup.cs doesn't know anything of T .
services to be registred:
services.AddSingleton<ApiCaller<AmazonResponseModel>, AmazonApiCaller>();
services.AddSingleton<ApiCaller<AppleResponseModel>, AppleApiCaller>();
services.AddTransient(typeof(ItemsController<>));
Change the controller as follows:
public class ItemsController<T> : ControllerBase
{
private readonly ApiHttpCaller<T> _apiCaller;
public ItemsController(ApiHttpCaller<T> apicaller){
_apiCaller = apicaller;
}
[HttpPost]
public async Task<ActionResult> Post([FromBody] string value)
{
// do something with the requested API Caller
}
}
This should now inject the correct ApiCaller into your service.
Of course you need to specify the type when injecting an ItemsController:
// Constructor
public AnyClass(ItemsController<AmazonResponseModel> controller){
// _apiCaller of controller will be AmazonApiCaller
}
Or maybe use another IoC Container like ninject.
You could benefit from Features like Contextual and named Bindings, which is documented on their page.
You DI registration is incorrect here. It should be like this:
services.AddSingleton<ApiCaller<AmazonResponseModel>, AmazonApiCaller>();
services.AddSingleton<ApiCaller<AppleResponseModel>, AppleApiCaller>();
you need to specify which generic would correspond to which implementation.
I'm using ThinkTecture's resource based authorization in my WebApi.
I'm trying to test one of my controller that I needed to check the access inside the function. But now, I can't test the function anymore since, I can't mock an extension method and since it's a nuget method, I can't modify the class to inject another value.
My controller look like this:
public class AlbumController : ApiController
{
public async Task<IHttpActionResult> Get(int id)
{
if (!(await Request.CheckAccessAsync(ChinookResources.AlbumActions.View,
ChinookResources.Album,
id.ToString())))
{
return this.AccessDenied();
}
return Ok();
}
}
And the ResourceAuthorizationManager is setted into the startup like this:
app.UseResourceAuthorization(new ChinookAuthorization());
Source code of the ThinkTecture project is here.
Thank you for your help
The ResourceAuthorizationAttribute uses Reqest.CheckAccess so I don't think it is a good solution to abstract away the implementation and then injecting it into the controller since in theory, the ResourceAuthorizationAttribute and the created service could use different implementations of the CheckAccess method.
I took a simpler approach by creating a BaseController
public class BaseController : ApiController
{
public virtual Task<bool> CheckAccessAsync(string action, params string[] resources)
{
return Request.CheckAccessAsync(action, resources);
}
}
and making CheckAccessAsync virtual so I can mock it (by for example Moq).
then from my controller
public class AlbumController : BaseController
{
public async Task<IHttpActionResult> Get(int id)
{
if (!(await CheckAccessAsync(ChinookResources.AlbumActions.View,
ChinookResources.Album,
id.ToString())))
{
return this.AccessDenied();
}
return Ok();
}
}
Unit testing the controller then is as easy as:
[TestClass]
public class TestClass
{
Mock<AlbumController> mockedTarget
AlbumController target
[TestInitialize]
public void Init()
{
mockedTarget = new Mock<AlbumController>();
target = mockedTarget.Object;
}
[Test]
public void Test()
{
mockedTarget.Setup(x => x.CheckAccessAsync(It.IsAny<string>(),
It.IsAny<string[]>()))
.Returns(Task.FromResult(true));
var result = target.Get(1);
// Assert
}
}
You could always wrap this static call into some abstraction of yours:
public interface IAuthorizationService
{
Task<bool> CheckAccessAsync(string view, string album, string id);
}
and then have some implementation that will delegate the call to the static extension method. But now since you will be working with the IAuthorizationService you can freely mock the CheckAccessAsync method in your unit tests.
As far as testing the implementation of this abstraction is concerned, you probably don't need it as it only acts as a bridge to the ThinkTecture's classes which should already be pretty well tested.
I finally solved my problem.
The real problem was that the CheckAccess method was an extension.
(for my answer, every class will refer to the sample that can be find here)
To stop using the extension method, I added these methods into my chinookAuthorization
public Task<bool> CheckAccessAsync(ClaimsPrincipal user, string action, params string[] resources)
{
var ctx = new ResourceAuthorizationContext(user ?? Principal.Anonymous, action, resources);
return CheckAccessAsync(ctx);
}
public Task<bool> CheckAccessAsync(ClaimsPrincipal user, IEnumerable<Claim> actions, IEnumerable<Claim> resources)
{
var authorizationContext = new ResourceAuthorizationContext(
user ?? Principal.Anonymous,
actions,
resources);
return CheckAccessAsync(authorizationContext);
}
Then I changed my controller to have an instance of the chinookAuthorization
public class AlbumController : ApiController
{
protected readonly chinookAuthorization chinookAuth;
public BaseApiController(chinookAuthorization chinookAuth)
{
if (chinookAuth == null)
throw new ArgumentNullException("chinookAuth");
this.chinookAuth = chinookAuth;
}
public async Task<IHttpActionResult> Get(int id)
{
if (!(await chinookAuth.CheckAccessAsync((ClaimsPrincipal)RequestContext.Principal, ChinookResources.AlbumActions.View,
ChinookResources.Album,
id.ToString())))
{
return this.AccessDenied();
}
return Ok();
}
}
And I'm still declaring my ChinookAuthorization into my owin startup, to keep using the same pattern for my attribute check access call.
So now, I just have to mock the chinookAuthorization, mock the response of the call to return true, and that's it!
In an attempt to DRY up my code today i'd like to do the following. (I don't know if its the best way, but it seems better than to have an ever increasing code base where I continually need to update multiple methods if i want to change something across the whole site)
What i know about Inheritance is scary. As Iv'e never questioned any of the code/libraries that I use, and Iv'e never really attempted writing anything like this before, but I want to learn... Hoping this will be my day of enlightenment :P
To my question:
Say Iv'e got an add method (in all my controllers) like this:
public ActionResult Add(VM_Down_Time_Capture viewModel)
{
using (Down_Time_CaptureRepository repository = new Down_Time_CaptureRepository())
{
if (!ModelState.IsValid)
return ReturnValidationFailure(ViewData.ModelState.Values);
Down_Time_Capture model = new Down_Time_Capture();
model.InjectFrom(viewModel);
string mserMsg = repository.Add(model, User.Identity.Name);
if (!string.IsNullOrEmpty(mserMsg))
return ReturnCustomValidationFailure(Server.HtmlEncode(mserMsg));
repository.Save();
return Json("Added successfully.", JsonRequestBehavior.AllowGet);
}
}
And at the moment I've got the following as well.
Generated by T4 Templates/EF.
ViewModels, Repositories, (Standard) EF Models
I'm thinking I need a ModelSpecfic base controller for each page (can be done using T4), that inherits from a custom ControllerBase class that contains the basic CRUD functionality. That way i can have custom code per controller, and my code base will be cleaner & smaller & that wont get affected should i need to regenerate the base files
I don't quite understand how to implement something in the lines of what i need. What i understand so far is that ill need to have my repositories, and view models inherit from a base as well and somehow specify in [B] which ones I'm using... but as to how to do that i don't know
For example (and this is my best attempt at it, not my actual code, extremely hacky as I'm amazingly confused :S)
public class Down_Time_CaptureController : Down_Time_CaptureBase
{
//[A]
}
//Generated by T4
public class Down_Time_CaptureBase: ControllerBase
{
//[B]
public override EntityObject CreateNewModel()
{
return new Down_Time_Capture();
}
public override Base_Repository CreateNewRepository()
{
return new Down_Time_CaptureRepository();
}
public override Base_ViewModel CreateNewViewModel()
{
return new VM_Down_Time_Capture();
}
//how would i go about specifying which repository & model & view model to use
//although i expect it to be something to what i did here above
//and how would i go about calling the new generic add method (but in context of this controller)?
}
//coded once
public abstract class ControllerBase: Controller
{
//[C]
//make abstract so i have to override it
public abstract Base_Controller CreateNewModel();
public abstract Base_Controller CreateNewRepository();
public abstract Base_Controller CreateNewViewModel();
//I'm assuming my generified add method would go in here
public virtual ActionResult Add(Base_ViewModel viewModel)
{
using (Base_Repository repository = CreateRepository())
{
if (!ModelState.IsValid)
return ReturnValidationFailure(ViewData.ModelState.Values);
EntityObject model = CreateNewModel();
model.InjectFrom(viewModel);
string mserMsg = repository.Add(model, User.Identity.Name);
if (!string.IsNullOrEmpty(mserMsg))
return ReturnCustomValidationFailure(Server.HtmlEncode(mserMsg));
repository.Save();
return Json("Added successfully.", JsonRequestBehavior.AllowGet);
}
}
}
Here's a simple generic interpretation of what you are asking for:
// concrete controller implementation
public class Down_Time_CaptureController: ControllerBase<Down_Time_Capture, VM_Down_Time_Capture, Down_Time_CaptureRepository>
{
}
// generic controller base
public abstract class ControllerBase<TModel, TViewModel, TRepository>: Controller
where TModel : Base_Model, new()
where TViewModel : Base_ViewModel, new()
where TRepository : Base_Repository, new()
{
protected virtual TModel CreateNewModel()
{
return (TModel)Activator.CreateInstance<TModel>();
}
protected virtual TRepository CreateNewRepository()
{
return (TRepository)Activator.CreateInstance<TRepository>();
}
protected virtual TViewModel CreateNewViewModel()
{
return (TViewModel)Activator.CreateInstance<TViewModel>();
}
//I'm assuming my generified add method would go in here
public virtual ActionResult Add(TViewModel viewModel)
{
using (var repository = CreateRepository())
{
if (!ModelState.IsValid)
return ReturnValidationFailure(ViewData.ModelState.Values);
var model = CreateNewModel();
model.InjectFrom(viewModel);
string mserMsg = repository.Add(model, User.Identity.Name);
if (!string.IsNullOrEmpty(mserMsg))
return ReturnCustomValidationFailure(Server.HtmlEncode(mserMsg));
repository.Save();
return Json("Added successfully.", JsonRequestBehavior.AllowGet);
}
}
}
A few notes:
You will probably want to create interfaces for the three types (Model, ViewModel, Repository) and use those as the generic constraints.
You will probably want a generic Repository interface and base implementation (so you don't have to code each repository independently, and copy similar logic from one to the other).
Consider using an Inversion of Control container and dependency injection. Rather than have the controller, for example, handle creating an instance of a repository, make it a property and set it from the constructor. You can then use an IoC of your choice (like Ninject or Autofac) and register concrete implementations, and it will manage creating and the lifetime of both the dependencies and the controller itself.
I have the following service interface:
public interface IGrantApplicationService
{
IEnumerable<T> GetAll();
}
Here is my implementation of the interface:
public class GrantApplicationService : IGrantApplicationService
{
public IEnumerable<GrantApplication> GetAll()
{
// Code here
}
public EditGrantApplicationViewModel CreateEditGrantApplicationViewModel()
{
// Code here
}
}
My controller:
public class GrantApplicationController : Controller
{
private IGrantApplicationService grantApplicationService;
public GrantApplicationController(IGrantApplicationService grantApplicationService)
{
this.grantApplicationService = grantApplicationService;
}
public ActionResult Create()
{
// I am trying to create my view model like this and populate it with data
EditGrantApplicationViewModel viewModel = grantApplicationService.CreateEditGrantApplicationViewModel();
return View(viewModel);
}
}
Please see in my Create method, I am trying to create my view model through the service, but when I click . then there doesn't seem to be an option to select CreateEditGrantApplicationViewModel, only GetAll is there. Why is this? I have all the correct references.
You need to define
EditGrantApplicationViewModel CreateEditGrantApplicationViewModel();
on your IGrantApplicationService interface.
At the moment the controller only knows about what is on the interface not the class.
Your grantApplicationService field is declared as IGrantApplicationService but your method CreateEditGrantApplicationViewModel is not in the interface. Just add CreateEditGrantApplicationViewModel in the interface and you'll be fine.