简体   繁体   中英

Swashbuckle/Swagger + ASP.Net Core: "Failed to load API definition"

I develop an ASP.NET Core 2 application and included Swagger. Everything worked fine until I introduced a method without explicitly defining the HTTP action:

public class ErrorController : Controller
{
    [Route("/error")]
    public IActionResult Index()
    {
        return StatusCode(500, new Error("Internal error."));
    }
}

When I started the app with this method, the following message showed up:

Failed to load API definition.

Errors
Fetch error Internal Server Error /swagger/v1/swagger.json

As soon as I explicitly set eg [HttpGet] the error disappears. The problem with this is, I need this method to fire for all possible HTTP operations. Of course, I could specify all operations explicitly, but I have the feeling Swagger should be able to handle this correctly.

Why does Swagger behave this way?

Is there any configuration I can use?

为每个 Action 方法添加 Httpxxx( [HttpGet] , [HttpPost] , ...) 属性,或[ApiExplorerSettings(IgnoreApi = true)]

Simply you can look into the log in the Output window. The actual error can be seen there in my case, I missed adding HTTP action on top of a methods

在此处输入图像描述

The option ResolveConflictingActions should be working on this case...

Here is the actual error:

System.NotSupportedException: Ambiguous HTTP method for action

That is coming from: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/86cc761bc4f5dda796f80ad8dfd8bc205542a6f6/src/Swashbuckle.AspNetCore.SwaggerGen/Generator/SwaggerGenerator.cs#L90

I think this is a bug, if you are truly interested you should report it to the project

Instead of blindy guessing what could be the issue, navigate to

http:///swagger/v1/swagger.json

在此处输入图像描述

In my case this could have been resolved by using the c.CustomSchemaIds(x => x.FullName);

which is a horrible workaround, but might be a quick fix for someone in need. My solution was to rename and clarify the path for those endpoints

I don't know if this has been resolved or not but one way you can go about this is by decorating the method with:

[ApiExplorerSettings(IgnoreApi = true)]

This will ensure that the method in question is ignored by Swagger.

Another possible issue is that the endpoint needs to be complete from the domain root.

I had:

app.UseSwaggerUI(c =>
{
     c.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs");
});

I had to use:

 app.UseSwaggerUI(c=>
{
     c.SwaggerEndpoint("/myApi/swagger/v1/swagger.json", "V1 Docs");

});

In ASP.NET Core, if there is a controller endpoint like:

[Route("images")]
[HttpGet("{id}")]

This can also fail with fetch failed. The solution is to have something like

[HttpGet("images/{id}")]

Same thing goes for HttpPost.

In addition to Helder Sepulvedas answer and from 'Monte-Christos' answer in this github issue - Actions require unique method/path combination for Swagger

I found the place to configure ResolveConflictingActions in ASP.NET Core apps. In your Setup class, add this to the ConfigureServices() method:

services.AddSwaggerGen(c => 
  { 
    other configs...;
    c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
  });

This done the trick for me!

Swagger also throws the same exception if there are public methods that are not actions in a controller. The fix is to make all of them protected or private or as mentioned add the attribute [ApiExplorerSettings(IgnoreApi = true)] .

in my case i use this code as like as .net code

[ActionName("Login")]
[HttpPost]

now i change it for use on net core web api

[HttpPost("Login")]

And it works right

I was also getting this error because i created a controller which dosent have [Route("api/[controller]")] . After putting it, the error went away.

app.UseSwagger()
   .UseSwaggerUI(c =>
   {
       c.SwaggerEndpoint("/swagger/v1/swagger.json", "DOA.API V1");
   });

对我有用的是将[NonAction]属性添加到我的控制器包装器中不是 API 调用的public方法中。

I had the same issue. In my case, All of my controllers inherited from a BaseController . in this base class, I got a public action which returns UserId according to Claims. I set the [NonAction] attribute on the top of this action.

[ApiController]
[ApiResultFilter]
[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

public class BaseController : ControllerBase
{

    [NonAction]
    public int GetCurrentUserId()
    {
        return int.Parse(this.User.Claims.First(p => p.Type == ClaimTypes.NameIdentifier).Value);
    }
}

Guess what... started getting this error page " Failed to load API definition ":

在此处输入图像描述

By looking at the app's console or the output window in Visual Studio it told me the following:

在此处输入图像描述

I added a public method called Client to the Web API BaseController and Swagger was thinking it should be included in the API.

To solve the problem: mark that method as protected :

在此处输入图像描述

Double check , if you have used same url name in your same controller. It was happened to my code

I also had this problem. I checked and applied all of the solutions for swagger config but the issue still remained. Finally, I checked the output panel and the problem was because of [DefaultValue("SYSDATETIMEOFFSET()")] .

The answer is here: Check the output panel and you will find the answer

The default api controller route configuration is the cause of this issue. When we add the API controller in the ASP.NET Core API application, by default it has controller-specific routes, which means it can support only a single method for each of the HTTP verbs Post, PUT, Delete, GET, and Patch.

There may be a requirement when we need to create more than one method having the Http verbs Post, PUT, Delete, GET, and Patch in a single controller, and if you create the method with the default route configuration, then you will get the following error while loading the Swagger UI.

在此处输入图像描述

The solution is that you have to change the default route configuration at the controller level when you have created more than one method with the HTTP verbs "post," "put," and "get." Delete or Put in the single API controller class.

Consider the following example: I have created the ASP.NET Core API application, which has by default one Get method, GetWeatherForecast, which returns the WeatherForecast. Then I have added one more Get method named WeatherForecastByCity in the default API Controller class without modifying the default route.

 [ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }

    [HttpGet(Name = "GetWeatherForecast")]
    public IEnumerable<WeatherForecast> Get()
    {
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }

    [HttpGet(Name = "WeatherForecastByCity")]
    public IEnumerable<WeatherForecast> Get(string city)
    {
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }
}

When we have run the application, we will get the Swagger loading error,

在此处输入图像描述

Now change the default route at controller level which can support more than one method having the Http verbs Post, PUT, Delete, GET, and Patch in a single controller.

[Route("api/[controller]/[action]")]

Also, remove the name of the method from the HTTP verb, which is defined using the Name attribute; just define the HTTP verb and proper method name.

Change from

 [HttpGet(Name = "GetWeatherForecast")]
    public IEnumerable<WeatherForecast> Get()
    {
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }

To

 [HttpGet]
    public IEnumerable<WeatherForecast> GetWeatherForecast()
    {
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }

Full Code

 [ApiController]
[Route("api/[controller]/[action]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IEnumerable<WeatherForecast> GetWeatherForecast()
    {
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }

     [HttpGet]
    public IEnumerable<WeatherForecast> WeatherForecastByCity(string city)
    {
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }
}

Now, run the application again after changing the default route, and you will see swagger loads without any issue.

在此处输入图像描述

This is answer 32 so there is a variety of reasons!

-> You need to troubleshoot efficiently

Solutions differ from some only working for NSwag/Swashbuckle to different versions of Swashbuckle to adding JsonIgnore of Newtonsoft vs built in or adding the BindNever attribute which all might or might not help but for sure it costs time!

Before you start: Check if there is maybe an exception or error message in debug console. This might speed you up the most. Sometimes there is none!

Speedy troubleshooting guide:

  1. All your Controllers should have a [ApiController] attribute (if not consider adding it) so search and replace [ApiController] with [ApiController][ApiExplorerSettings(IgnoreApi = true)] to make them invisible for SwaggerGen

OptionA - Working now without any api controllers:

  1. Open your favorite Git tool that has a user interface and dismiss half of the changes in git until swagger loads https://127.0.0.1:12345/swagger/index.html again and that way rule it down to a single controller that breaks swashbuckle
  2. Comment out all Get/Post/Put/Delete actions within this single controller and check if it is working.
  3. Comment back in half of that actions until it is no longer working and that way rule it down to a single action
  4. Comment out the whole content of the Action and just return Ok(); to see if response is the problem.
  5. Try with a replacement request class that just has a fraction of the props and narrow it down to a property causing the problem.

OptionB - Still not working even without any api controllers:

  1. Try to reproduce with a fresh minimal project using the same package version and make the new project more and more similar to your big one. Your problem is likely already the initialisation which might be broken eg due to breaking changes or incompatibility within packages.

When you have the root cause the solution is often obvious

In my case the root cause was that I had a property of type "Type" in the request POCO which was failing without leaving any hint about what the problem is. The solution was to change it to a function which even made more sense than how it was.

If you know how to improve this guide write it in the comments.

I was getting a TypeLoadException on a class that I was deleting that was unused. The fix in my case was to delete the bin/obj/Debug folder contents. Clean solution + rebuild solution did not fix for me.

My error reason was that same url name,

 [HttpGet("get/LeaveCommand/{id}")]

I use same url and swagger cannot get them

 [HttpGet("get/LeaveCommand/{id}")]

It happened because of Newtonsoft.Json in my case. But the thing is I was not using it. One of the packages may be depend on it but I did not have time to check.

So just check the output panenl solve the related issue.

If you have in your models (request or response) properties of type which inherits/implements types like System.ComponentModel (or other types) this will throw an error

"The JSON property 'item' is defined multiple times on type"...

Try to ignore this property using [JsonIgnore] attribute of Newtonsoft.Json

In my case i had a getter of type DataTable

In the Startup file you need to make sure that you add

services.AddSwaggerDocument();

before you add

app.UseOpenApi();
app.UseSwaggerUi3();

or it can result in this error

Fetch error undefined /swagger/{documentName}/swagger.json

for core 3 I had the same issue and was really confused that problem was in slash.

Configuration was:

services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "my-API", Version = "v1" });
            });

This swagger endpoint threw the message of TS:

app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/v1/swagger.json", "my-API v1");
            });

And finally I got it worked with removing the first slash in the URL:

app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("v1/swagger.json", "my-API v1");
            });

For me the problem was having registering swagger after MapControllers middleware. Once I moved before it worked.

For me it was a very simple issue, i had 3 [HttpGet] methods which turned out i needed to change the "createOrder" method to a [HttpPost]

        [HttpGet]
        public async Task<ActionResult<List<Order>>> GetOrders()
        {
            return await _context.Orders.Include(o => o.OrderItems).Where(x => x.BuyerId == User.Identity.Name).ToListAsync();
        }

        [HttpGet("{id}", Name = "GetOrder")]
        public async Task<ActionResult<Order>> GetOrder(int id)
        {
            return await _context.Orders.Include(x => x.OrderItems).Where(x => x.BuyerId == User.Identity.Name && x.Id == id).FirstOrDefaultAsync();
        }

        [HttpPost]
        public async Task<ActionResult<int>> CreateOrder(CreateOrderDto orderDto)
        {
            var basket = await _context.Baskets.RetrieveBasketWithItems(User.Identity.Name).FirstOrDefaultAsync();
            var items = new List<OrderItem>();
}

对我来说,它是通过添加 HttpGet 属性和删除索引方法来解决的。

The issue for me was caused by lazy/frustrated refactoring and I was able to determine the issue by reading the debug console when running the API in debugger mode.

Due to the bad refactoring, I ended up with two models with the same name, and Swagger was getting confused.

I had something like:

PROJECT
├───Auth
│       AutheController.cs
│       UserDto.cs
│
└───Controllers
    │   MyContrller.cs
    │
    └───Models
            UserDto.cs

Having two UserDto models is what was confusing Swagger. Cleaning this up fixed the issues.

在此处输入图像描述

And the logs.....

System.ArgumentOutOfRangeException: Count cannot be less than zero. (Parameter 'count')

And in my case, the Route definition was wrong.

Before (returns error) Note the lack of {curly braces} in the Route definition.

[HttpGet]
[Route("id:guid")]

After (all good - no error)

[HttpGet]
[Route("{id:guid}")]

I was getting below error in .net core web api:

Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Failed to generate Operation for action - WebApplication9.Controllers.AnimalsController.Post (WebApplication9). See inner exception

Solution:

Make sure to use the different class/name per models, I was passing the same name with different properties. So swagger json schemas wasn't identifying the correct one.

Example:

UserController.cs

[HttpPost, Route("Create")]
public IActionResult Create(CreateRequest model) {}

PersonController.cs

[HttpPost, Route("Create")]
public IActionResult Create(CreateRequest model) {}

So, we need to use different models as parameter such as UserCreateRequest and PersonCreateRequest .

When you use Swagger is necessary to add [ApiExplorerSettings(IgnoreApi = true)] in your code like this:

    [Route("/error")]
    [ApiExplorerSettings(IgnoreApi = true)]
    public IActionResult HandleError() =>
        Problem();

Make sure you methods in controller are marked with [HttPost] and/or [HttpGet]

use the [HttpGet("Post")] or [HttpGet("Get")] in your Actions

I've got the error mentioned in the question and didn't find in answers the simple reason like:

Endpoints should have unique routes ie distinct by name, VERB etc.

在此处输入图像描述

Swagger is struggled when 2 endpoints routs looks the same

Check recently added endpoint(s) if they are not duplicating from routing perspective (name, verbs etc.)

in my case i was experimenting and did a copy-paste like this:

在此处输入图像描述

once i've made them distinct routes, Swagger was ok

在此处输入图像描述

the solution is simply specify a unique route

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