简体   繁体   中英

Issue generating swagger.json file when inheriting controller actions from a base controller

I am writing a web api in .net 6. The api has 32 controllers for getting lookup values from a database. I have a service layer that talks to the repository and maps the entity to the response object. Meaning the functionality for 90% of the controllers is exactly the same, outside of the entity that is being retrieved. So, I moved all of the common code to a generically typed controller base and am now using inheritance to implement the correct type.

Basically, I have moved the Get, GetById, Create and Update methods to the base controller and have inherited that class for the individual controllers.

Executing the web api using Postman works as expected. If I execute the Get method on the AddressType route, versus the EmailType route, it returns the appropriate list.

However, trying to get the swagger definition is failing miserably with the following:

An unhandled exception occurred while processing the request. IndexOutOfRangeException: Index was outside the bounds of the array. Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.InitializeTypeInformation()

Up to moving to inheritance, I was able to get the swagger definition correctly.

I've looked at other similar questions and tried a handful of their solutions. But, either they are talking about older versions of WebApi/.net or do not apply.

code edited for length

public abstract class HubControllerEntityBase<TEntity, TCreate, TUpdate, TResponse, TCategoryName, TService> : HubControllerBase<TCategoryName>, IHubController<TEntity, TCreate, TUpdate>
    where TEntity : class, IHubEntity, new()
    where TCreate : class, ICreateEntityRequest, new()
    where TUpdate : class, IUpdateEntityRequest, new()
    where TResponse : class, IEntityResponse, new()
    where TCategoryName : class
    where TService : IHubEntityService<IGetEntityRequest, TCreate, TUpdate, TResponse>
{
    #region Constructors

    /// <summary>
    ///     The primary constructor.
    /// </summary>
    /// <param name="logger">The logger to use for logging and debugging.</param>
    /// <param name="dependencies">The controller dependencies.</param>
    /// <param name="service">The service used for processing the requests.</param>
    protected HubControllerEntityBase(
        ILogger<TCategoryName> logger,
        HubControllerDependencies dependencies,
        TService service
    ) : base(logger, dependencies)
    {
        Service = service;
    }

    #endregion

    #region Public methods

    /// <summary>
    ///     Gets all of the entities in the data store.
    /// </summary>
    /// <param name="options">
    ///     An <see cref="ODataQueryOptions" /> object that contains the filters, sorts, etc applied from the query string.
    /// </param>
    /// <returns>An <see cref="IEnumerable{T}" /> list of entities.</returns>
    /// <response code="200">The query was processed successfully.</response>
    /// <response code="500">A system exception occurred executing the query.</response>
    [HttpGet]
    [ProducesResponseType(typeof(IEnumerable<>), 200)]
    [ProducesResponseType(500)]
    public virtual async Task<IActionResult> GetAll(ODataQueryOptions<TEntity> options)
    {
        // Initialize the event id structure and log entry
        var ev = new EventId(LogEventId, MethodBase.GetCurrentMethod()?.Name);
        Logger.LogDebug(ev, $"Request: {ev.Name}");

        // Get the list of entities, passing in the OData options
        var response = await Service.GetAsync(options);

        // Return the response
        return GetResponseResult(response);
    }

    #endregion
}

[ApiController]
[ApiVersion("1.0")]
[Route(ApiRoutes.AddressType.ControllerRoute)]
[Route(ApiRoutes.AddressType.ControllerRouteWithVersion)]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class AddressTypeController : HubControllerEntityNameBase<AddressType, AddressTypeRequest, AddressTypeRequest, AddressTypeResponse, AddressTypeController,
    IAddressTypeService<AddressTypeRequest, AddressTypeResponse>>
{
    public AddressTypeController(
        ILogger<AddressTypeController> logger,
        HubControllerDependencies dependencies,
        IAddressTypeService<AddressTypeRequest, AddressTypeResponse> service
    ) : base(logger, dependencies, service)
    {
        LogEventId = 60100;
    }
}

Any help is greatly appreciated.

As it turns out, there were three separate issues.

The first was mine. Despite it compiling successfully, there was a reference to an empty IEnumerable<> in the schema definition. This was causing the initial swagger/API explorer failure:

[HttpGet]
[ProducesResponseType(typeof(IEnumerable<>), 200)]
[ProducesResponseType(500)]

There were two other issues that needed to be addressed in the swagger configuration: unique schema names, and odata. Fortunately, swagger config can handle both. They were resolved by adding the following lines to my swagger options:

services.AddSwaggerGen(x =>
{
    x.CustomSchemaIds(x => x.FullName);
    x.MapType(typeof(ODataQueryOptions<>), () => new());
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM