Swagger 2与OpenAPI 3

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swagger 2与OpenAPI 3相关的知识,希望对你有一定的参考价值。

参考技术A

重命名
swagger: 2.0
openAPI: 3.0.0

Swagger 2.0 基础URL结构

OpenAPI 3.0 基础URL结构

我们可以定义一个基础url,通过里面装载变量值(类似于路径模版),在使用时,通过variables属性可以定义变量值,当然也可以给定默认值

Swagger 2中的definitions概念在OpenAPI 3中标准化为【组件】,可以在多个地方重复使用且可定义,组件列表如下:

Swagger 2

Swagger 2最容易混淆的方面之一是body / formData。它们是参数的子集,只能有一个或另一个,如果你使用body,格式与参数的其余部分不同(只能使用body参数,名称不相关,格式不同,等等)

OpenAPI 3

现在,body已经被移入了它自己的叫做requestBody的部分,并且formData也已经被合并到里面。另外,cookies已经被添加为参数类型(除了现有的标题,路径和查询选项之外)。

requestBody有很多新的功能。现在可以提供example(或数组examples)for requestBody。这是非常灵活的(你可以传入一个完整的例子,一个参考,甚至是一个URL的例子)。

新的requestBody支持不同的媒体类型(content是一个MIME_Types的数组,像application/json或者text/plain,当然你也可以用 / 捕捉所有)。

对于参数,你有两个选择你想如何定义它们。你可以定义一个“模式”(像原来2.0那样),可以尽情地描述项目。如果更复杂,可以使用“requestBody”中的“content”。

通配符的出现,我们可以以“4XX”来定义响应,而不必单独定义每个响应码。
响应和响应头可以更复杂。可以使用“content”对象(如在请求中)的有效载荷。

链接是OpenAPI 3最有趣的补充之一。它有点复杂,但可能非常强大。这基本上是描述“下一步是什么”的一种方式。

比方说,你得到一个用户,它有一个addressId。这addressId本身是无用的。您可以使用链接来展示如何“扩大”,并获得完整的地址。

在“/ users / userId”的响应中,我们找回了一个addressId。“链接”描述了如何通过引用“$ response.body#/ addressId”来获取地址。

另一个用例是分页。如果要获取100个结果,links可以显示如何获得结果101-200。它是灵活的,这意味着它可以处理任何分页方案limits来cursors。

Swagger 2

OpeanAPI 3

一堆安全性的变化!它已被重命名,OAuth2流名已更新,您可以有多个流,并且支持OpenID Connect。“基本”类型已被重命名为“http”,现在安全可以有一个“方案”和“bearerFormat”。

飞机票至 Swagger 2

https://blog.readme.io/an-example-filled-guide-to-swagger-3-2/
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#oasDocument

https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#componentsObject

swagger : 无法加载 API 定义 undefined /swagger/v1/swagger.json

【中文标题】swagger : 无法加载 API 定义 undefined /swagger/v1/swagger.json【英文标题】:swagger : Failed to load API definition undefined /swagger/v1/swagger.json 【发布时间】:2019-12-23 23:34:00 【问题描述】:

我尝试在我的 asp.net 核心 api 中配置 swagger 并收到以下错误。未能加载 API 定义 undefined /swagger/v1/swagger.json

我不确定为什么会收到此错误。我已经在启动文件中添加了必要的配置

我尝试了以下路径,但没有任何区别

/swagger/v1/swagger.json
../swagger/v1/swagger.json
v1/swagger.json

startup.cs

public class Startup
    
        public Startup(IConfiguration configuration)
        
            Configuration = configuration;
        

        public IConfiguration Configuration  get; 

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);


            services.AddSwaggerGen(c =>
            

            );


            services.AddDbContext<NorthwindContext>(item => item.UseSqlServer(Configuration.GetConnectionString("NorthwindDBConnection")));
            services.AddCors(option => option.AddPolicy("MyPolicy", builder => 
                builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();

            ));

            var mappingConfig = new MapperConfiguration(mc =>
            
                mc.AddProfile(new MappingProfile());
            );

            IMapper mapper = mappingConfig.CreateMapper();
            services.AddSingleton(mapper);



            services.AddScoped<ICustomerRepository, CustomerRepository>();
        

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        
            if (env.IsDevelopment())
            
                app.UseDeveloperExceptionPage();
            
            else
            
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            
            app.UseCors("MyPolicy");
            app.UseHttpsRedirection();
            app.UseSwagger();
            app.UseSwaggerUI(c =>  c.SwaggerEndpoint("/swagger/v1/swagger.json", "API name"); );
            app.UseMvc();
        
    

客户控制器

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Customer.Repository;
using CustomerService.Models;
using CustomerService.ViewModel;
using Microsoft.AspNetCore.Mvc;

namespace CustomerService.Controllers

    [Route("api/[controller]")]
    [ApiController]
    public class CustomersController : Controller
    

        ICustomerRepository _customersRepository;


        public CustomersController(ICustomerRepository customersRepository)
        
            _customersRepository = customersRepository;
        

        [HttpGet]
        [Route("GetCustomers")]
        //[NoCache]
        [ProducesResponseType(typeof(List<CustomerViewModel>), 200)]
        [ProducesResponseType(typeof(ApiResponse), 400)]
        public async Task<IActionResult> Customers()
        
            try
            
                var customers = await _customersRepository.GetAllCustomers();
                if (customers == null)
                
                    return NotFound();
                

                return Ok(customers);
            
            catch
            
                return BadRequest();
            
        


        [HttpGet]
        [Route("GetCustomer")]
        //[NoCache]
        [ProducesResponseType(typeof(List<CustomerViewModel>), 200)]
        [ProducesResponseType(typeof(ApiResponse), 400)]
        public async Task<IActionResult> Customers(string customerId)
        
            if (customerId == null)
            
                return BadRequest();
            

            try
            
                var customer = await _customersRepository.GetCustomer(customerId);
                if (customer == null)
                
                    return NotFound();
                

                return Ok(customer);
            
            catch
            
                return BadRequest();
            
        

        [HttpPost]
        [Route("AddCustomer")]
        public async Task<IActionResult> AddCustomer([FromBody] CustomerViewModel model)
        
            if (ModelState.IsValid)
            
                try
                
                    var customerId = await _customersRepository.Add(model);
                    if (customerId != null)
                    
                        return Ok(customerId);
                    
                    else
                    
                        return NotFound();
                    
                
                catch(Exception ex)
                
                    return BadRequest();
                
            
            return BadRequest();
        


        [HttpPost]
        [Route("DeleteCustomer")]
        public async Task<IActionResult> DeleteCustomer(string customerId)
        
            int result = 0;

            if (customerId == null)
            
                return BadRequest();
            

            try
            
                var customer = await _customersRepository.Delete(customerId);
                if (customer == null)
                
                    return NotFound();
                

                return Ok(customer);
            
            catch
            
                return BadRequest();
            
        



        [HttpPost]
        [Route("UpdateCustomer")]
        public async Task<IActionResult> UpdateCustomer([FromBody] CustomerViewModel model)
        
            if (ModelState.IsValid)
            
                try
                
                    await _customersRepository.Update(model);
                    return Ok();
                
                catch(Exception ex)
                
                    if (ex.GetType().FullName == "Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException")
                    
                        return NotFound();
                    

                    return BadRequest();
                
            
            return BadRequest();
        
    




【问题讨论】:

希望这将帮助其他遇到同样错误的人。我收到此错误是因为我的控制器中有多个 HttpGet 方法,但我没有正确地将控制器方法归因于不同的路由模板。将路由模板添加到我的方法的 HttpGet 属性解决了我的问题,例如 [HttpGet("my-distinct-route")]。 【参考方案1】:

您遇到了一个错误。因为你把你的动作名称加倍了。看看这个例子。 Swagger – Failed To Load API Definition ,更改 [Route("GetCustomers")] 名称并重试。

【讨论】:

我将路由和操作名分别更改为 [Route("GetCustomerById")] 和 GetCustomerById 但同样的错误 我在那个网站上展示了你如何理解这个问题。您能否提供可以在浏览器控制台中找到的错误屏幕截图@Tom 对于它的价值,这个答案确实帮助了我。我的控制器中有两个 Get 具有相同的路由(两个都没有定义路由参数)。当我将路由参数添加到第二个 Get 时,Swagger 错误得到了解决。【参考方案2】:

Swagger 也不能处理两个具有相同名称的类(至少,不是开箱即用的)。因此,如果您有两个名称空间,并且有两个具有相同名称的类,它将无法初始化。

【讨论】:

【参考方案3】:

如果您在损坏的 Swashbuckle 页面上,请打开开发工具...查看 Swagger 发回的 500 响应,您将获得一些深刻的见解。

这就是我正在做的愚蠢的事情......在 HTTPGet 中有一个路由以及一个 ROUTE 路由。

    [HttpGet("id")]
    [ProducesResponseType(typeof(string), 200)]
    [ProducesResponseType(500)]
    [Route("employeeID:int")]

【讨论】:

【参考方案4】:

我知道这已解决,但我今天遇到了同样的问题。

在我的例子中,问题是我创建了一个基控制器类来继承其他控制器。 当我在基类上创建公共函数时,问题开始发生。把它变成受保护的就行了

【讨论】:

【参考方案5】:

这通常表示 Swashbuckle 出于某种原因不支持的控制器/操作。

预计您的项目中没有 swagger.json 文件。 Swashbuckle 使用 ASP.NET Core 的 ApiExplorer API 动态创建和提供服务。这里可能发生的是 Swashbuckle 无法生成 Swagger.json,因此 UI 无法显示。

很难确切地知道是什么导致了失败,因此最好的调试方法可能只是删除一半的控制器(只需将文件移动到临时位置)并检查问题是否仍然存在。然后你就会知道你的哪一半控制器包含了麻烦的动作。您可以“二进制搜索”删除控制器(然后是操作),直到找出导致 Swashbuckle 无法生成 Swagger.json 的操作方法。一旦你知道了,这应该是你代码中的一些问题还是应该在 Swashbuckle 存储库中提交的问题。

你可以按F12打开chrome浏览器的开发者工具查看失败原因,然后输入失败的请求路径,点击错误文件预览详细错误。

这也可能是路线不明确或类似 Swashbuckle 绊倒的问题。一旦您将失败的原因缩小到更具体的原因,就可以根据需要修复或归档。

【讨论】:

【参考方案6】:

如果你想通过host:port/swagger/v1/swagger.json访问swagger,那么你应该在里面添加options: SwaggerGenOptions

public void ConfigureServices(IServiceCollection services)

    services.AddSwaggerGen(c => 
        c.SwaggerDoc("swagger/v1", new OpenApiInfo  Version = "1.0", Title = "API" );
    );

它应该可以正常工作。

【讨论】:

以上是关于Swagger 2与OpenAPI 3的主要内容,如果未能解决你的问题,请参考以下文章

.NET Core基础篇之:集成Swagger文档与自定义Swagger UI

Swagger与OpenAPI

Swagger与OpenAPI

如何在 OpenAPI / Swagger 中递归引用封闭类型定义?

为啥 `additionalProperties` 是在 Swagger/OpenAPI 2.0 中表示 Dictionary/Map 的方式

如何将 OpenAPI Spec (Swagger 2.0) 转换为 proto3?