核心: “加载 API 定义失败”

我开发了一个 ASP.NET Core 2应用程序,其中包括了 Swagger。一切都很好,直到我引入了一个没有明确定义 HTTP 动作的方法:

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

当我用这种方法启动应用程序时,出现了以下信息:

加载 API 定义失败。

错误
获取错误内部服务器错误/swagger/v1/swagger.json

一旦我显式地设置例如 [HttpGet],错误就会消失。这样做的问题是,我需要这个方法来触发所有可能的 HTTP 操作。 当然,我可以明确地指定所有的操作,但我觉得斯威格应该能够正确地处理这个问题。

斯威格为什么会这样?

有什么配置我可以用吗?

199852 次浏览

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

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.

Add Httpxxx([HttpGet], [HttpPost], ...) attribute for each Action method, or [ApiExplorerSettings(IgnoreApi = true)]

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.

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)].

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");
});

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.

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");


});

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}")]

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

enter image description here

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!

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

What worked for me is adding [NonAction] attribute to the public methods that are not API calls in my controller wrapper.

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.

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 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);
}
}

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

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

enter image description here

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 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

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>();
}

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

enter image description here

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

enter image description here

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:

enter image description here

For me it was solved by adding the HttpGet attribute and removing the index methods.

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.

enter image description here

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]

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.

enter image description here

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,

enter image description here

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.

enter image description here

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 ABC0 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 e.g. 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.