Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generated Documentation uses baseclass name instead of actual controller name #1650

Closed
ThaDaVos opened this issue Oct 4, 2018 · 11 comments
Closed
Milestone

Comments

@ThaDaVos
Copy link

ThaDaVos commented Oct 4, 2018

For all my CRUD based controllers I'm using a generic controller base class - this base class extends ControllerBase
When generating the Swagger it uses this base class name and throws all routes of the controllers which extend this class under it - how can I fix this so it uses the controller names?

image

@RicoSuter
Copy link
Owner

Are you using the api explorer based generator? Can you post a sample controller and base controller?

@ThaDaVos
Copy link
Author

ThaDaVos commented Oct 4, 2018

I'm using the API explorer based generator, here are the BaseCrudController and the DevicesController

[Authorize(Policy = "ApiUser")]
public class BaseCrudController<TModel, TCommunicationModel> : ControllerBase where TModel : class, IModel, new() where TCommunicationModel : class, ICommunicationEntity, new()
{
    private readonly IGenericRepository<TModel> _repository;
    private readonly IMapper _mapper;

    public BaseCrudController(IGenericRepository<TModel> repository, IMapper mapper)
    {
        _repository = repository;
        _mapper = mapper;
    }

    [HttpGet]
    public virtual async Task<IActionResult> Get()
    {
        var @entities = await _repository.GetAll().ToArrayAsync();
        return Ok(new MultiEntityReadResponse<TCommunicationModel>(ToCommunications(@entities)));
    }

    [HttpGet("{id}")]
    public virtual async Task<IActionResult> Get([FromRoute] Guid id)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var @entity = await _repository.GetById(id);

        if (@entity == null) return NotFound();

        return Ok(new SingleEntityReadResponse<TCommunicationModel>(ToCommunication(@entity)));
    }

    [HttpPut("{id}")]
    public virtual async Task<IActionResult> Put([FromRoute] Guid id, [FromBody] SingleEntityUpdateRequest<TCommunicationModel> request)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var @model = request.Entity;

        if (!@model.Id.Equals(id))
        {
            return BadRequest();
        }

        try
        {
            var @entity = await _repository.Update(id, ToModel(@model));
            return Ok(new SingleEntityUpdateResponse<TCommunicationModel>(ToCommunication(@entity)));
        }
        catch (RowNotInTableException)
        {
            return NotFound();
        }
    }

    [HttpPost]
    public virtual async Task<IActionResult> Post([FromBody] SingleEntityCreateRequest<TCommunicationModel> request)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        
        var @model = request.Entity;

        var @entity = await _repository.Create(ToModel(@model));
        return CreatedAtAction(
            "Get", 
            new { id = @entity.Id }, 
            new SingleEntityCreateResponse<TCommunicationModel>(ToCommunication(@entity)));
    }

    [HttpDelete("{id}")]
    public virtual async Task<IActionResult> Delete([FromRoute] Guid id)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var @entity = await _repository.Delete(id);
        if (@entity == null) return NotFound();
        return Ok(new SingleEntityDeleteResponse<TCommunicationModel>(ToCommunication(@entity)));
    }

    private TModel ToModel(TCommunicationModel model) => _mapper.Map<TModel>(model);
    private TModel[] ToModels(TCommunicationModel[] models) => _mapper.Map<TModel[]>(models);
    private TCommunicationModel ToCommunication(TModel model) => _mapper.Map<TCommunicationModel>(model);
    private TCommunicationModel[] ToCommunications(TModel[] models) => _mapper.Map<TCommunicationModel[]>(models);
}
[Route("api/devices")]
[SwaggerTag("Devices")]
public class DevicesController : BaseCrudController<Device, Communication.Entities.Device>
{
    public DevicesController(IGenericRepository<Device> repository, IMapper mapper) : base(repository, mapper)
    {
    }
}

@0ChristopherCampbell
Copy link

Also need a solution to this. Broke in the latest version I think. Was working fine before then

@RicoSuter RicoSuter added this to the vNext milestone Oct 9, 2018
@RicoSuter
Copy link
Owner

What’s the expected name?

@ThaDaVos
Copy link
Author

ThaDaVos commented Oct 9, 2018

@RSuter The expected name is the actual controller name - not the base class name - so in this instance it would be Devices because it's the DevicesController

@RicoSuter
Copy link
Owner

@ThaDaVos
Copy link
Author

That or you shouldn't look at the highest level class and not baseclasses

@RicoSuter
Copy link
Owner

I’d expect that the ControllerType is the highest level/actual controller type. Need to write a test and debug to see why this is not the case

@0ChristopherCampbell
Copy link

0ChristopherCampbell commented Oct 11, 2018

Note that if you define an API endpoint in the controller inheriting from the base controller it will display properly as belonging to that controller.

e.g. PersonController has a unique method defined GetByDateOfBirth, it will add a new controller entry for Person containing only that GetByDateOfBirth end point. The rest of the inherited methods will be put under BaseController

So it looks like it's grabbing the controller name from wherever the method is defined.

Interesting that it doesn't cut out the "Controller" from the BaseController in swagger UI and adds '2 (or in my case '3) to the name. I'm wondering if this 'number is related to amount of inheritance as I have a PersonController : BaseController( : BaseReadOnlyController) instead of just the one base controller.

@ThaDaVos
Copy link
Author

Sorry to re-open this but I'm using app.UseSwaggerUi3WithApiExplorer and that fix seems only to be for the reflection based one, if I want to use that one I'm getting warnings it's deprecated...
Also, getting errors because my routes are returning generic objects:
System.ArgumentException: Type TCommunicationModel contains generic parameters Parameter name: type at NSwag.AspNetCore.Middlewares.WebApiToSwaggerMiddleware.GenerateSwaggerAsync(HttpContext context) at NSwag.AspNetCore.Middlewares.WebApiToSwaggerMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

@ThaDaVos
Copy link
Author

Also I can't even use the code like you do in the commit, app.UseSwagger needs an argument
image

@RicoSuter RicoSuter mentioned this issue Nov 15, 2018
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants