1

I am working on a webapi application on .net Core and I have a base controller from which all other controller derives from.

Here is the class:

public class ReadOnlyBaseController<TEntity, TEntityResource> : Controller
{
    private readonly IMapper mapper;
    private readonly IBaseUnitOfWork unitOfWork;
    private readonly IBaseRepository<TEntity> repository;
    public ReadOnlyBaseController(IBaseRepository<TEntity> repository, IBaseUnitOfWork unitOfWork,
                            IMapper mapper)
    {
        this.repository = repository;
        this.unitOfWork = unitOfWork;
        this.mapper = mapper;
    }

    [HttpGet]
    [Authorize]
    virtual public async Task<IActionResult> Get()
    {
        List<TEntity> TEntitys = await this.repository.GetTodos();
        return Ok(TEntitys);
    }

    [HttpGet("Id")]
    [Authorize]
    virtual public IActionResult GeSingle(int Id)
    {
        TEntity tEntity = this.repository.GetSingle(Id);
        TEntityResource tEntityResource = this.mapper.Map<TEntity, TEntityResource>(tEntity);
        return Ok(tEntityResource);
    }
}

However, some of my API endpoints do not require the Authorize attribute. So I created another base controller:

public class ReadOnlyNoOAuthBaseController<TEntity, TEntityResource> : Controller
{
    private readonly IMapper mapper;
    private readonly IBaseUnitOfWork unitOfWork;
    private readonly IBaseRepository<TEntity> repository;
    public ReadOnlyNoOAuthBaseController(IBaseRepository<TEntity> repository, IBaseUnitOfWork unitOfWork,
                            IMapper mapper)
    {
        this.repository = repository;
        this.unitOfWork = unitOfWork;
        this.mapper = mapper;
    }

    [HttpGet]
    virtual public async Task<IActionResult> Get()
    {
        List<TEntity> TEntitys = await this.repository.GetTodos();
        return Ok(TEntitys);
    }

    [HttpGet("Id")]
    virtual public IActionResult GeSingle(int Id)
    {
        TEntity tEntity = this.repository.GetSingle(Id);
        TEntityResource tEntityResource = this.mapper.Map<TEntity, TEntityResource>(tEntity);
        return Ok(tEntityResource);
    }
}

As you probably noticed, other than the [Authorize] attribute, the controllers are identical. Is there any way to make this work without the need to create a new controller?

Cheers!

  • Create the base controller with no attributes. then on the controller that needs auth you can put it that one. – Nkosi Jan 11 '18 at 00:17
  • 1
    Yet another argument why you should [never use a base controller in MVC](https://stackoverflow.com/a/6119341/). – NightOwl888 Jan 11 '18 at 00:20
  • What exactly is the point of having the same API exposed in another controller but without authorization? Why do these APIs exist *with* authorization in the first place when I can also consume *the same ones* without authorization? – poke Jan 11 '18 at 08:19

1 Answers1

1

Create the base controller with no authorizations attributes.

public class ReadOnlyBaseController<TEntity, TEntityResource> : Controller {
    protected readonly IMapper mapper;
    protected readonly IBaseUnitOfWork unitOfWork;
    protected readonly IBaseRepository<TEntity> repository;

    public ReadOnlyBaseController(
        IBaseRepository<TEntity> repository, IBaseUnitOfWork unitOfWork, IMapper mapper) {
        //...
    }

    [HttpGet]
    public virtual async Task<IActionResult> Get() {
        //..
    }

    [HttpGet("Id")]
    public virtual IActionResult GeSingle(int Id) { 
        //...
    }
}

Then in derived controllers that need auth you can add it on the controller itself

[Authorize]
public class ReadOnlyOAuthController<TEntity, TEntityResource> : ReadOnlyBaseController<TEntity, TEntityResource> {

    public ReadOnlyOAuthController(
        IBaseRepository<TEntity> repository, IBaseUnitOfWork unitOfWork, IMapper mapper) 
            : base(repository, unitOfWork, mapper) {
    }

    [AllowAnonymous]
    [HttpGet("someaction")]
    public IAction SomeOtherAction() {
        //...
    }
}

the [Authorize] attribute will apply to all actions on the derived controller and if you want to allow an action you can use the [AllowAnonymous] attribute.

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Brilliant, thank you very much! I've marked this as the answer to my question. – Jaime Calderón Jan 11 '18 at 00:52
  • 2
    Alternatively you can mark the `ReadOnlyBaseController` class with the `[Authorize]` attribute which ensures all routes are secure and then use the `[AllowAnonymous]` attribute on action methods for any non-secure routes. – Brad Jan 11 '18 at 05:10
  • 1
    That's also a good idea. I'd rather forget to remove the Authorize attribute than to accidentally leave an api endpoint exposed. – Jaime Calderón Jan 11 '18 at 16:16