1

我正在尝试在 ASP.NET 样板框架中进行版本控制。

我在 Swagger Gen 中创建了两个版本(“v1.0”和“v2.0”)并为 Web API 设置了 API 版本,但每次我从 Swagger 获得两个版本的所有 API 时。

启动.cs

AddSwaggerGenConfigureServices()

services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1.0", new Info { Title = "My API", Version = "v1.0" });
    options.SwaggerDoc("v2.0", new Info { Title = "My API", Version = "v2.0" });
    options.DocInclusionPredicate((docName, description) => true);

    // Define the BearerAuth scheme that's in use
    options.AddSecurityDefinition("bearerAuth", new ApiKeyScheme()
    {
        Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
        Name = "Authorization",
        In = "header",
        Type = "apiKey"
    });
    // Assign scope requirements to operations based on AuthorizeAttribute
    options.OperationFilter<SecurityRequirementsOperationFilter>();
});

UseSwaggerUI在配置()中:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseAbp(options => { options.UseAbpRequestLocalization = false; }); 

    app.UseCors(_defaultCorsPolicyName); 

    app.UseStaticFiles();

    app.UseAuthentication();

    app.UseAbpRequestLocalization();

    app.UseSignalR(routes =>
    {
        routes.MapHub<AbpCommonHub>("/signalr");
    });

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "defaultWithArea",
            template: "{area}/{controller=Home}/{action=Index}/{id?}");

        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });

    app.UseSwagger();

    app.UseSwaggerUI(options =>
    {
        options.InjectOnCompleteJavaScript("/swagger/ui/abp.js");
        options.InjectOnCompleteJavaScript("/swagger/ui/on-complete.js");
        options.SwaggerEndpoint("/swagger/v1.0/swagger.json", "My API V1.0");
        options.SwaggerEndpoint("/swagger/v2.0/swagger.json", "My API V2.0");
    }); 
}

API 控制器 - v1.0

[ApiVersion("v1.0")]
[Route("/api/invoicemodule/1.0/[controller]")]
public class InvoiceController : MyControllerBase
{       
    [HttpGet, MapToApiVersion("v1.0")]
    public IActionResult GetInvoiceById(string invoiceid)
    {
        //BusinessService.SparePartHistoryService sas = new BusinessService.SparePartHistoryService(_logger, _localizer, _configuration);

        if (string.IsNullOrEmpty(invoiceid)) return BadRequest("'Id' cannot be null or empty.");

        try
        {
            BusinessModels.Invoice sp = new BusinessModels.Invoice
            {
                Id = ""
            };
            if (sp != null)
            {
                return Ok(sp);
            }
            else
            {
                return NotFound();
            }
        }
        catch (Exception e)
        {
            return BadRequest(e.Message);
        }
    }
}

API 控制器 - v2.0

[ApiVersion("v2.0")]
[Route("/api/invoicemodule/2.0/[controller]")]
public class InvoiceController : MyControllerBase
{
    [HttpGet, MapToApiVersion("v2.0")]
    public IActionResult GetInvoiceById(string invoiceid)
    {
        //BusinessService.SparePartHistoryService sas = new BusinessService.SparePartHistoryService(_logger, _localizer, _configuration);

        if (string.IsNullOrEmpty(invoiceid)) return BadRequest("'Id' cannot be null or empty.");

        try
        {
            BusinessModels.Invoice sp = new BusinessModels.Invoice
            {
                Id = ""
            };
            if (sp != null)
            {
                return Ok(sp);
            }
            else
            {
                return NotFound();
            }
        }
        catch (Exception e)
        {
            return BadRequest(e.Message);
        }
    }
}

输出

大摇大摆的版本 v1 输出

在此处输入图像描述

4

3 回答 3

3

是的,我得到了解决方案

参考:https ://github.com/domaindrivendev/Swashbuckle.AspNetCore

我在 Startup.cs 文件的 ConfigurationService() 方法中更改了 Service.AddSwaggerGen() 的少量代码

在我更改的代码中添加的注释

      services.AddSwaggerGen(options =>
        {
            options.SwaggerDoc("v1", new Info { Version = "1" }); // Version must require in integer. can't set Version = "v1"
            options.SwaggerDoc("v2", new Info { Version = "2" });
            options.DocInclusionPredicate((docName, description) =>
            { // insert this {} code instead of "true" 
                var versions = description.ControllerAttributes()
                .OfType<ApiVersionAttribute>()
                .SelectMany(attr => attr.Versions);

                return versions.Any(v => $"v{v.ToString()}" == docName);
            });
        });
于 2018-04-26T11:26:51.360 回答
1

步骤 1. 创建 ApiVersion1RoutePrefixAttribute.cs 类

public class ApiVersion1RoutePrefixAttribute: RoutePrefixAttribute {
    private const string RouteBase = "api/{apiVersion:apiVersionConstraint(v1)}";
    private const string PrefixRouteBase = RouteBase + "/";

    public ApiVersion1RoutePrefixAttribute(string routePrefix):base (string.IsNullOrWhiteSpace(routePrefix)?RouteBase: PrefixRouteBase+routePrefix) {
    }
}

步骤 2. 创建 ApiVersion2RoutePrefixAttribute.cs 类

public class ApiVersion1RoutePrefixAttribute: RoutePrefixAttribute {
    private const string RouteBase = "api/{apiVersion:apiVersionConstraint(v1)}";
    private const string PrefixRouteBase = RouteBase + "/";

    public ApiVersion1RoutePrefixAttribute(string routePrefix):base (string.IsNullOrWhiteSpace(routePrefix)?RouteBase: PrefixRouteBase+routePrefix) {
    }
}

步骤 3. 创建类 ApiVersionConstraint

public class ApiVersionConstraint : IHttpRouteConstraint {

    public string AllowedVersion { get; private set; }
    public ApiVersionConstraint(string allowedVersion) {
        this.AllowedVersion = allowedVersion.ToLowerInvariant();
    }

    public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection) {
        object value;
        if(values.TryGetValue(parameterName,out value)&& value != null) {
            return AllowedVersion.Equals(value.ToString().ToLowerInvariant());
        }
        return false;
    }
}

步骤 4 创建一个类 NamespaceHttpControllerSelector.cs

public class NamespaceHttpControllerSelector : IHttpControllerSelector {
    private readonly HttpConfiguration _configuration;
    private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers;

    public NamespaceHttpControllerSelector(HttpConfiguration config) {
        _configuration = config;
        _controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary);
    }

    public HttpControllerDescriptor SelectController(HttpRequestMessage request) {
        var routeData = request.GetRouteData();
        if (routeData == null) {
            throw new HttpResponseException(HttpStatusCode.NotFound);
        }
        var controllerName = GetControllerName(routeData);
        if (controllerName == null) {
            throw new HttpResponseException(HttpStatusCode.NotFound);
        }
        var namespaceName = GetVersion(routeData);
        if (namespaceName == null) {
            throw new HttpResponseException(HttpStatusCode.NotFound);
        }
        var controllerKey = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", namespaceName, controllerName);
        HttpControllerDescriptor controllerDescriptor;
        if (_controllers.Value.TryGetValue(controllerKey, out controllerDescriptor)) {
            return controllerDescriptor;
        }
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }

    public IDictionary<string, HttpControllerDescriptor> GetControllerMapping() {
        return _controllers.Value;
    }

    private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary() {
        var dictionary = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
        var assembliesResolver = _configuration.Services.GetAssembliesResolver();
        var controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();
        var controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);
        foreach (var controllerType in controllerTypes) {
            var segments = controllerType.Namespace.Split(Type.Delimiter);
            var controllerName = controllerType.Name.Remove(controllerType.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length);
            var controllerKey = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", segments[segments.Length - 1], controllerName);

            if (!dictionary.Keys.Contains(controllerKey)) {
                dictionary[controllerKey] = new HttpControllerDescriptor(_configuration,
                controllerType.Name,
                controllerType);
            }
        }
        return dictionary;
    }

    private T GetRouteVariable<T>(IHttpRouteData routeData, string name) {
        object result;
        if (routeData.Values.TryGetValue(name, out result)) {
            return (T)result;
        }
        return default(T);
    }

    private string GetControllerName(IHttpRouteData routeData) {
        var subroute = routeData.GetSubRoutes().FirstOrDefault();
        if (subroute == null) return null;
        //((HttpActionDescriptor[])subroute.Route.DataTokens["actions"]).First()
        var dataTokenValue = subroute.Route.DataTokens["actions"];

        if (dataTokenValue == null)
            return null;

        var controllerName = ((HttpActionDescriptor[])dataTokenValue).First().ControllerDescriptor.ControllerName.Replace("Controller", string.Empty);

        return controllerName;
    }

    private string GetVersion(IHttpRouteData routeData) {
        var subRouteData = routeData.GetSubRoutes().FirstOrDefault();
        if (subRouteData == null) return null;
        return GetRouteVariable<string>(subRouteData, "apiVersion");
    }
}

第 5 步。您的 WebApiConfig.cs 文件应如下所示

public static void Register(HttpConfiguration config) {



    var constraintResolver = new DefaultInlineConstraintResolver();

    constraintResolver.ConstraintMap.Add("apiVersionConstraint", typeof(ApiVersionConstraint));

    config.MapHttpAttributeRoutes(constraintResolver);

    config.Services.Replace(typeof(IHttpControllerSelector), new NamespaceHttpControllerSelector(config));
}

第 6 步。你的控制器应该是这样的

您的版本 1 控制器

namespace AgentExperienceAPI.Controllers.v1 {

    [Route("GetStatus")]
    [AcceptVerbs("GET")]
    public HttpResponseMessage GetStatus() {

        return Request.CreateResponse(HttpStatusCode.OK, new Dictionary<string, object> {
            { "Status", "OK" }
        });
    }
}

您的第 2 版控制器

namespace AgentExperienceAPI.Controllers.v1 {

    [Route("GetStatus")]
    [AcceptVerbs("GET")]
    public HttpResponseMessage GetStatus() {

        return Request.CreateResponse(HttpStatusCode.OK, new Dictionary<string, object> {
            { "Status", "OK" }
        });
    }
}

在此处输入图像描述 您的控制器应位于两个单独的文件夹中,请参见图片

Swagger 将处理剩下的事情

于 2018-04-26T10:14:52.083 回答
0

您是否尝试过如下配置Swashbuckle ?

options.DocInclusionPredicate((docName, apiDesc) =>
{
      if (!apiDesc.ActionDescriptor.IsControllerAction())
      {
           return false;
      }

      switch (docName)
      {
           case "v1":
            return apiDesc.GroupName == null || apiDesc.GroupName == "v1";
           case "v2":
            return apiDesc.GroupName == null || apiDesc.GroupName == "v2";
           default:
            return false;
      }
});

最后在你的控制器上:

    [ApiExplorerSettings(GroupName = "v2")]
    [Route("api/[controller]/[action]")]
    public class PublicAppService : ASAAppServiceBase, IPublicAppService
    {
        public PublicAppService()
        {
        }

        [HttpGet]
        public string Test()
        {
            return "test";
        }

    }

讨论过

https://github.com/aspnetboilerplate/aspnetboilerplate/issues/3814#issuecomment-418118210

于 2020-07-04T12:41:52.713 回答