在我们的API中,当用户调用endpoint时,我们希望从外部Nuget包返回一个对象。
这个对象(可以在这里查看)有几个属性。其中一个叫做动作
。此属性具有as typeIPaymentResponseAction
,但可以是一组不同的操作类型(您可以在这里看到它们)。
生成的swagger不知道这些操作,也不生成所需的代码。即使设置了多态性设置。
services.AddSwaggerGen(c =>
{
c.EnableAnnotations();
c.UseOneOfForPolymorphism();
});
有没有办法让这些东西在我的大摇大摆中出现?也许有一些定制的招摇选项?
使用c. SelectSubTypes使用
代码在第一次回答后更新
Adyen.Model.Checkout.PaymentResponse": {
"type": "object",
"properties": {
"resultCode": {
"$ref": "#/components/schemas/Adyen.Model.Checkout.PaymentResponse.ResultCodeEnum"
},
"action": {
"oneOf": [
{
"$ref": "#/components/schemas/Adyen.Model.Checkout.Action.IPaymentResponseAction"
},
{
"$ref": "#/components/schemas/Adyen.Model.Checkout.Action.CheckoutAwaitAction"
},
{
"$ref": "#/components/schemas/Adyen.Model.Checkout.Action.CheckoutDonationAction"
},
{
"$ref": "#/components/schemas/Adyen.Model.Checkout.Action.CheckoutOneTimePasscodeAction"
},
{
"$ref": "#/components/schemas/Adyen.Model.Checkout.Action.CheckoutQrCodeAction"
},
{
"$ref": "#/components/schemas/Adyen.Model.Checkout.Action.CheckoutRedirectAction"
},
{
"$ref": "#/components/schemas/Adyen.Model.Checkout.Action.CheckoutSDKAction"
},
{
"$ref": "#/components/schemas/Adyen.Model.Checkout.Action.CheckoutThreeDS2Action"
},
{
"$ref": "#/components/schemas/Adyen.Model.Checkout.Action.CheckoutVoucherAction"
}
],
"nullable": true
}......
而IPayment响应行动是:
"Adyen.Model.Checkout.Action.IPaymentResponseAction": {
"required": [
"type"
],
"type": "object",
"properties": {
"type": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false,
"discriminator": {
"propertyName": "type",
"mapping": {
"await": "#/components/schemas/Adyen.Model.Checkout.Action.CheckoutAwaitAction",
"donation": "#/components/schemas/Adyen.Model.Checkout.Action.CheckoutDonationAction",
"oneTimePasscode": "#/components/schemas/Adyen.Model.Checkout.Action.CheckoutOneTimePasscodeAction",
"qrCode": "#/components/schemas/Adyen.Model.Checkout.Action.CheckoutQrCodeAction",
"redirect": "#/components/schemas/Adyen.Model.Checkout.Action.CheckoutRedirectAction",
"sdk": "#/components/schemas/Adyen.Model.Checkout.Action.CheckoutSDKAction",
"threeDS2Action": "#/components/schemas/Adyen.Model.Checkout.Action.CheckoutThreeDS2Action",
"voucher": "#/components/schemas/Adyen.Model.Checkout.Action.CheckoutVoucherAction"
}
}
},
更新:我现在所有的行动看起来都是这样的,所以我认为还没有。但是很接近了!
"CheckoutAwaitAction": {
"type": "object",
"allOf": [
{
"$ref": "#/components/schemas/Rig.Commercial.Reservation.Core.Settings.Swagger.Swagger_Models.PaymentResponseAction"
}
],
"additionalProperties": false
}
这是解决问题的问题的更新答案:)很抱歉的长帖子。
您描述的问题是由于缺乏Swashuckle
处理C#接口以反映多态层次结构的能力造成的(对我来说似乎是一个缺失的功能)。
这里有一个变通方法(请参见此处的MVP项目)。
c.EnableAnnotations(enableAnnotationsForInheritance: true, enableAnnotationsForPolymorphism: true);
c.UseAllOfToExtendReferenceSchemas();
c.UseAllOfForInheritance();
c.UseOneOfForPolymorphism();
Swashback
不将接口视为“父”类型。如果我们让它“思考”它仍然在处理一个类而不是一个接口呢?让我们介绍一下PaymentResponseAction
class:
[DataContract]
[SwaggerDiscriminator("type")]
public class PaymentResponseAction : IPaymentResponseAction
{
[JsonProperty(PropertyName = "type")]
public string Type { get; set; }
}
在AddSwaggerGen
调用中,我们还应该提供正确的鉴别器选项:
c.SelectDiscriminatorNameUsing(type =>
{
return type.Name switch
{
nameof(PaymentResponseAction) => "type",
_ => null
};
});
c.SelectDiscriminatorValueUsing(subType =>
{
return subType.Name switch
{
nameof(CheckoutAwaitAction) => "await",
nameof(CheckoutBankTransferAction) => "bank",
nameof(CheckoutDonationAction) => "donation",
nameof(CheckoutOneTimePasscodeAction) => "oneTimePasscode",
// rest of the action types ...
_ => null
};
});
到目前为止,一切都很正常。唯一缺少的是实现类的allOf
关键字。目前,仅使用Swashback
的选项是不可能的,因为它在构造allOf
时使用BaseType来解析子类型。
和以前一样,我们可以让Swashback认为它处理的是继承的类型。我们可以生成“假”类型,这些类型继承我们新的PaymentResponseAction
类,并从我们感兴趣的实现类型复制属性。这些“假”类型不必是功能性的;它们应该包含足够的类型信息,以使swashback
感到高兴。
下面是一个实现此功能的方法的示例。它接受源类型以从基类型复制属性,并返回新类型。使用自定义的附加设置,也可以使用自定义的附加属性。
请注意,此代码应进行改进,以便为生产做好准备;例如,它不应该“复制”具有JsonIgnore
或类似属性的公共属性。
private static Type GenerateReparentedType(Type originalType, Type parent)
{
var assemblyBuilder =
AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("hack"), AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("hack");
var typeBuilder = moduleBuilder.DefineType(originalType.Name, TypeAttributes.Public, parent);
foreach (var property in originalType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
var newProperty = typeBuilder
.DefineProperty(property.Name, property.Attributes, property.PropertyType, null);
var getMethod = property.GetMethod;
if (getMethod is not null)
{
var getMethodBuilder = typeBuilder
.DefineMethod(getMethod.Name, getMethod.Attributes, getMethod.ReturnType, Type.EmptyTypes);
getMethodBuilder.GetILGenerator().Emit(OpCodes.Ret);
newProperty.SetGetMethod(getMethodBuilder);
}
var setMethod = property.SetMethod;
if (setMethod is not null)
{
var setMethodBuilder = typeBuilder
.DefineMethod(setMethod.Name, setMethod.Attributes, setMethod.ReturnType, Type.EmptyTypes);
setMethodBuilder.GetILGenerator().Emit(OpCodes.Ret);
newProperty.SetSetMethod(setMethodBuilder);
}
var customAttributes = CustomAttributeData.GetCustomAttributes(property).ToArray();
foreach (var customAttributeData in customAttributes)
{
newProperty.SetCustomAttribute(DefineCustomAttribute(customAttributeData));
}
}
var type = typeBuilder.CreateType();
return type ?? throw new InvalidOperationException($"Unable to generate a re-parented type for {originalType}.");
}
private static CustomAttributeBuilder DefineCustomAttribute(CustomAttributeData attributeData)
{
// based on https://stackoverflow.com/a/3916313/8607180
var constructorArguments = attributeData.ConstructorArguments
.Select(argument => argument.Value)
.ToArray();
var propertyArguments = new List<PropertyInfo>();
var propertyArgumentValues = new List<object?>();
var fieldArguments = new List<FieldInfo>();
var fieldArgumentValues = new List<object?>();
foreach (var argument in attributeData.NamedArguments ?? Array.Empty<CustomAttributeNamedArgument>())
{
var fieldInfo = argument.MemberInfo as FieldInfo;
var propertyInfo = argument.MemberInfo as PropertyInfo;
if (fieldInfo != null)
{
fieldArguments.Add(fieldInfo);
fieldArgumentValues.Add(argument.TypedValue.Value);
}
else if (propertyInfo != null)
{
propertyArguments.Add(propertyInfo);
propertyArgumentValues.Add(argument.TypedValue.Value);
}
}
return new CustomAttributeBuilder(
attributeData.Constructor, constructorArguments,
propertyArguments.ToArray(), propertyArgumentValues.ToArray(),
fieldArguments.ToArray(), fieldArgumentValues.ToArray()
);
}
现在我们可以在AddSwaggerGen
调用中使用它来使Swashuckle
以我们想要的方式解析这些类型:
var actionTypes = new[]
{
GenerateReparentedType(typeof(CheckoutAwaitAction), typeof(PaymentResponseAction)),
GenerateReparentedType(typeof(CheckoutBankTransferAction), typeof(PaymentResponseAction)),
GenerateReparentedType(typeof(CheckoutDonationAction), typeof(PaymentResponseAction)),
GenerateReparentedType(typeof(CheckoutOneTimePasscodeAction), typeof(PaymentResponseAction)),
// rest of the action types ...
};
c.SelectSubTypesUsing(type =>
{
var allTypes = typeof(Startup).Assembly.GetTypes().ToArray();
return type.Name switch
{
nameof(PaymentResponseAction) => new[] { typeof(PaymentResponseAction) }.Union(actionTypes),
nameof(IPaymentResponseAction) => new[] { typeof(PaymentResponseAction) }.Union(actionTypes),
_ => allTypes.Where(t => t.IsSubclassOf(type))
};
});
现在Swashuckle
应该正确生成所有内容:
paths:
/api/someEndpoint:
get:
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/PaymentResponse'
# ...
components:
schemas:
PaymentResponse:
type: object
properties:
resultCode:
allOf:
- $ref: '#/components/schemas/ResultCodeEnum'
nullable: true
action:
oneOf:
- $ref: '#/components/schemas/PaymentResponseAction'
- $ref: '#/components/schemas/CheckoutAwaitAction'
- $ref: '#/components/schemas/CheckoutBankTransferAction'
- $ref: '#/components/schemas/CheckoutDonationAction'
- $ref: '#/components/schemas/CheckoutOneTimePasscodeAction'
# ... rest of the actions
nullable: true
# ... rest of the properties
PaymentResponseAction:
required:
- type
type: object
properties:
type:
type: string
nullable: true
additionalProperties: false
discriminator:
propertyName: type
mapping:
await: '#/components/schemas/CheckoutAwaitAction'
bank: '#/components/schemas/CheckoutBankTransferAction'
donation: '#/components/schemas/CheckoutDonationAction'
oneTimePasscode: '#/components/schemas/CheckoutOneTimePasscodeAction'
# ... rest of the action mapping
CheckoutAwaitAction:
type: object
allOf:
- $ref: '#/components/schemas/PaymentResponseAction'
properties:
# CheckoutAwaitAction's own properties
additionalProperties: false
CheckoutBankTransferAction:
type: object
allOf:
- $ref: '#/components/schemas/PaymentResponseAction'
properties:
# CheckoutBankTransferAction's own properties
additionalProperties: false
CheckoutDonationAction:
type: object
allOf:
- $ref: '#/components/schemas/PaymentResponseAction'
properties:
# CheckoutDonationAction's own properties
additionalProperties: false
CheckoutOneTimePasscodeAction:
type: object
allOf:
- $ref: '#/components/schemas/PaymentResponseAction'
properties:
# CheckoutOneTimePasscodeAction's own properties
additionalProperties: false
# ... rest of the action classes
这可以使用Swashuckle来完成。AspNetCore。注释
包。根据API设计,您可以使用以下方法之一。
这种方法利用了在响应模式中使用其中一个的优势。这样做的目的是让Swashback生成一个响应模式,该模式将有一个:
responses:
'200':
description: Success
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/CheckoutAwaitAction'
- $ref: '#/components/schemas/CheckoutBankTransferAction'
- $ref: '#/components/schemas/CheckoutDonationAction'
- $ref: '#/components/schemas/CheckoutOneTimePasscodeAction'
# ...
以下是您需要做的:
>
向您的AddSwaggerGen
调用添加UseOneOfForPolymorism
和SelectSubTypes使用
选项;确保您的SelectSubTypes使用
解析IPayment响应动作
接口到您的API从控制器方法返回的所有所需实现:
services.AddSwaggerGen(c =>
{
// ...
c.UseOneOfForPolymorphism();
c.SelectSubTypesUsing(baseType =>
{
if (baseType == typeof(IPaymentResponseAction))
{
return new[]
{
typeof(CheckoutAwaitAction),
typeof(CheckoutBankTransferAction),
typeof(CheckoutDonationAction),
typeof(CheckoutOneTimePasscodeAction),
// ...
};
}
return Enumerable.Empty<Type>();
});
向控制器方法添加SwaggerResponse
注释。仅指定IPaymentResponseAction
接口。
[HttpGet]
[SwaggerResponse((int)HttpStatusCode.OK, "response description", typeof(IPaymentResponseAction))]
public IPaymentResponseAction GetPaymentAction()
{
// ...
这将在Swagger UI
中为您提供所需的模式:
在Swagger-UI响应
请注意,Swagger UI
不支持“示例值”部分,如果模式有一个oneOf
定义:它只会显示selectSubsubstancessing
调用中第一个解析类型的响应样本。
看起来不像是你的案子,但我还是想提一下。
如果不同响应代码的响应模式不同,则可以直接在控制器中指定相应的类型:
[HttpPost]
[SwaggerResponse((int)HttpStatusCode.Created, "response description", typeof(CheckoutAwaitAction))]
[SwaggerResponse((int)HttpStatusCode.OK, "response description", typeof(CheckoutBankTransferAction))]
// ...
public IPaymentResponseAction PostPaymentAction()
{
// ...
当我使用POST方法向我的服务器发出多部分请求时,请求工作正常。 下面是我如何设置我的JMeter: 发帖请求 然而,当我试图向同一个URL发出PUT请求时,该请求不起作用。 提交请求 服务器上的错误是 org.springframework.web.multipart.MultipartException:当前请求不是多部分请求 这似乎是JMeter上的一个问题,因为当我尝试使用Postman执
我有以下类层次结构: 我正在尝试读取并锁定一个具体实例,以便其他事务无法读取它,使用hibernate。 现在的函数: 正在工作-为刷新操作生成“SELECT for UPDATE”语法。 refresh()和get()函数之间的不同之处在于get()函数使用外部左联接来选择具体对象,而refresh()使用内部联接来选择具体对象。 在悲观锁定的上下文中,这些连接之间有区别吗?
我想设置pythonpath,但它不适用于其他目录。 我的朋友: 导出PYTHONPATH=/usr/lib/python2.7 导出PYTHONPATH=$PYTHONPATH/plat-linux2:$PYTHONPATH/lib-dynload:$PYTHONPATH/dist-包:$PYTHONPATH/lib-tk:$PYTHONPATH 如果我只保留第一行(单个目录) export P
我正在weblogic 10.3中部署一个ear应用程序,其格式为分解格式,支持快速交换,并处于开发模式。ear文件还包含一个分解格式的web应用程序。对web应用程序中的JSP所做的更改正在重新加载。但更改后,不会重新加载web inf下的类。 weblogic部署配置如下所示。weblogic-application.xml的内容 应用ear/META-INF中的xml内容 战争中的weblo
问题内容: 我想做的事: 我想计算一个使用多类问题 我试图做的是: 这是使用虹膜数据集制作的可复制示例。 我一个热编码我的目标 我使用决策树分类器 最后我执行交叉val 失败的原因: 最后一行抛出以下错误 我的环境: python == 3.7.2 sklearn == 0.19.2 我的问题: 是一个错误,还是我在误用? 问题答案: scikit-learn的交叉验证功能不必要的麻烦在于,默认情