我正在尝试在ASP.NET Boilerplate框架中进行版本控制。
我在Swagger Gen中创建了两个版本(#34; v1.0"和#34; v2.0")并为Web API设置API版本,但每次我都获得所有API来自Swagger的两个版本。
Startup.cs :
AddSwaggerGen
中的 ConfigureServices()
:
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>();
});
Configure()中的 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);
}
}
}
答案 0 :(得分:2)
是的,我得到了解决方案
参考: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);
});
});
答案 1 :(得分:0)
步骤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将处理其余的
答案 2 :(得分: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