【问题标题】:Using Nswag with Odata creates error when try to access swagger endpoint尝试访问 swagger 端点时,将 Nswag 与 Odata 一起使用会产生错误
【发布时间】:2020-05-08 06:32:53
【问题描述】:

我们正在尝试在我们的 asp.net 核心 API 项目中使用 Nswag 和 Odata。我们可以使用 Nswag 获取 API 文档,也可以使用 Odata 来简化查询。但是当我们同时使用它们并尝试访问 API swagger 文档 (https://localhost:5001/swagger/index.html) 时,它会生成此错误:

这是我的启动文件:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;


using Microsoft.AspNet.OData.Extensions;
using System.Web.Http;
using Microsoft.OpenApi.Models;
using System.Reflection;
using Newtonsoft.Json;
using Microsoft.AspNet.OData.Builder;
using GL.Data.Models.EntityClass;
using Microsoft.Net.Http.Headers;
using Microsoft.AspNet.OData.Formatter;
using Newtonsoft.Json.Serialization;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
using Newtonsoft.Json.Converters;

namespace GL.service
{
    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)
            .AddJsonOptions(options =>
            {
                // Use camel case properties in the serializer and the spec (optional)
                options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
                // Use string enums in the serializer and the spec (optional)
                options.SerializerSettings.Converters.Add(new StringEnumConverter());
            });

            // registers a Swagger v2.0 document with the name "v1" (default)
            services.AddSwaggerDocument(c => { 
            c.DocumentName = "V1";
                c.Title = "GL Controller";
            }); 
            services.AddOData();

        }

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

                app.UseHsts();
            }
            app.UseCors(b => b.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().AllowCredentials());
            app.UseHttpsRedirection();
            app.UseAuthentication();

            app.UseOpenApi(); // Serves the registered OpenAPI/Swagger documents by default on 
            app.UseSwaggerUi3(); // Serves the Swagger UI 3 web ui to view the OpenAPI/Swagger 
            app.UseMvc(routeBuilder =>
            {
                routeBuilder.EnableDependencyInjection();
                routeBuilder.Expand().Select().Filter().Count().OrderBy();
            });
        }
    }
}

请帮忙解决这个问题。

【问题讨论】:

    标签: c# swagger odata asp.net-core-2.2 nswag


    【解决方案1】:

    这是一个workaround,您可以在加载 NSwag UI 时修复 API 错误。但是 Swashbuckle for AspNetCore 不支持 OData,并且任何 OData 端点都不会显示在您的 NSwag UI 中。

    services.AddOData();
    
    services.AddMvcCore(options =>
    {
        foreach (var outputFormatter in options.OutputFormatters.OfType<ODataOutputFormatter>().Where(_ => _.SupportedMediaTypes.Count == 0))
        {
            outputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
        }
        foreach (var inputFormatter in options.InputFormatters.OfType<ODataInputFormatter>().Where(_ => _.SupportedMediaTypes.Count == 0))
        {
            inputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
        }
    });
    

    记得把services.AddOData();放在services.AddMvcCore()之前。

    参考https://github.com/OData/WebApi/issues/1177

    【讨论】:

      【解决方案2】:

      如果它仍然不起作用,您可能需要添加其他解决方法: .Net 5.0 和 Microsoft.AspNetCore.OData 8.0.0-preview3

          services.AddSwaggerGen(c =>
          {
              c.DocInclusionPredicate((docName, apiDesc) =>
              {
                  // Filter out 3rd party controllers
                  var assemblyName = ((ControllerActionDescriptor)apiDesc.ActionDescriptor).ControllerTypeInfo.Assembly.GetName().Name;
                  var currentAssemblyName = GetType().Assembly.GetName().Name;
                  return currentAssemblyName == assemblyName;
              });
      
              c.SwaggerDoc("v1", new OpenApiInfo { Title = ApplicationConstant.APP_NAME, Version = "v1" });
          });
      

      第一个解决方法:

      services.AddOData();
      
      services.AddMvcCore(options =>
      {
          foreach (var outputFormatter in options.OutputFormatters.OfType<ODataOutputFormatter>().Where(_ => _.SupportedMediaTypes.Count == 0))
          {
              outputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
          }
          foreach (var inputFormatter in options.InputFormatters.OfType<ODataInputFormatter>().Where(_ => _.SupportedMediaTypes.Count == 0))
          {
              inputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
          }
      });
      

      不需要使用 AddMvcCore,也可以将代码放在 AddControllers 中。

      【讨论】:

        【解决方案3】:

        此问题的根本原因是 OData 创建了多个具有相同名称的路由,这会导致 Swagger 关闭。为了解决这个问题,您需要使用自定义操作处理器。在下面给出的代码中,我为 odata 与 api 使用了不同的路由(例如,/odata/country 与 /api/country,两者都调用具有 [EnableQuery] 属性的相同 GET 方法。

        在您的启动中,定义您的 Odata 路由:

                services.AddControllers(options =>
                {
                })
                .AddOData(opt => opt.AddRouteComponents("Odata", GetEdmModel()).Filter().Select().Expand());
        

        为了在您的 Swagger 文档中包含 Odata 路由,请在您的项目中添加一个新类(我将我的称为“AddOdataOperationProcessor”):

        public class AddOdataOperationProcessor : IOperationProcessor
        {
            private readonly List<string> AddedOdataPaths;
            private readonly string OdataPath;
        
            public AddOdataOperationProcessor(string odataPath)
            {
                AddedOdataPaths = new List<string>();
                OdataPath = odataPath.ToLower();
            }
        
            public bool Process(OperationProcessorContext context)
            {
                if (context.OperationDescription.Operation.ExtensionData == null)
                    context.OperationDescription.Operation.ExtensionData = new Dictionary<string, object>();
        
                if (context.OperationDescription.Path.ToLower().StartsWith(OdataPath))
                {
                    if (!AddedOdataPaths.Contains(context.OperationDescription.Path))
                    {
                        AddedOdataPaths.Add(context.OperationDescription.Path);
                        return true;
                    }
                    else
                    {
                        context.AllOperationDescriptions.Remove(context.OperationDescription);
                        return false;
                    }
                }
                return true;
            }
        }
        

        最后,添加一个带有自定义操作处理器的 Swagger 文档。请注意,我们的自定义处理器需要 OData 路径作为输入。这应该与您在启动时添加 OData 时使用的路径相匹配(如上所示)并以“/”开头:

        services.AddSwaggerDocument(config =>
                {
                    config.OperationProcessors.Add(new AddOdataOperationProcessor("/Odata"));
                    config.PostProcess = document =>
                    {
                        document.Info.Version = "v1";
                        document.Info.Title = "Include Odata";
                        document.Info.Description = "Includes OData";
                        document.Info.TermsOfService = "None";
                        document.Info.Contact = new NSwag.OpenApiContact
                        {
                            Name = "Kevat shah",
                            Email = string.Empty,
                            Url = "https://marketplace.goldensuncorp.com"
                        };
                        document.Info.License = new NSwag.OpenApiLicense
                        {
                            //TODO
                            Name = "Use under LICX",
                            Url = "https://example.com/license"
                        };
                    };
                });
        

        工作原理:对于 Swagger 找到的每个路径(包括重复的 OData 路径)调用一次此自定义处理器。处理器根据您将路径添加到 Swagger 文档配置时输入的路径来识别路径是否为 OData 路径(在此示例中,路径为“/Odata”)。变量 AdditionalODataPaths 跟踪已添加的路径并忽略已添加的路径。

        编辑:如果您想忽略 Swagger 文档的所有 OData 路由而不是添加它们,请使用此类:

        public class RemoveOdataOperationProcessor : IOperationProcessor
        {
            private readonly string OdataPath;
        
            public RemoveOdataOperationProcessor(string odataPath)
            {
                OdataPath = odataPath.ToLower();
            }
        
            public bool Process(OperationProcessorContext context)
            {
                if (context.OperationDescription.Operation.ExtensionData == null)
                    context.OperationDescription.Operation.ExtensionData = new Dictionary<string, object>();
        
                if (context.OperationDescription.Path.ToLower().StartsWith(OdataPath))
                {
                    context.AllOperationDescriptions.Remove(context.OperationDescription);
                    return false;
                }
                return true;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-14
          • 1970-01-01
          • 2019-04-06
          • 1970-01-01
          相关资源
          最近更新 更多