gpt4 book ai didi

c# - 在运行时创建 C# Web API 2 Controller 时出现异常

转载 作者:太空宇宙 更新时间:2023-11-03 12:20:06 25 4
gpt4 key购买 nike

我想在启动时动态生成 W​​eb API 2 Controller 。在本例中,我使用了反射。

但是,当我编译它并启动服务时,当我通过 HttpRequest 调用它时出现异常。异常看起来像这样:

{
"Message": "An error has occurred.",
"ExceptionMessage": "Unable to cast object of type 'System.Object[]' to type 'System.Web.Http.ParameterBindingAttribute[]'.",
"ExceptionType": "System.InvalidCastException",
"StackTrace": " at System.Web.Http.Controllers.ReflectedHttpParameterDescriptor.GetCustomAttributes[TAttribute]() at System.Web.Http.Controllers.HttpParameterDescriptor.FindParameterBindingAttribute() at System.Web.Http.Controllers.HttpParameterDescriptor.get_ParameterBinderAttribute() at System.Web.Http.ModelBinding.DefaultActionValueBinder.GetParameterBinding(HttpParameterDescriptor parameter) at System.Array.ConvertAll[TInput,TOutput](TInput[] array, Converter`2 converter) at System.Web.Http.ModelBinding.DefaultActionValueBinder.GetBinding(HttpActionDescriptor actionDescriptor) at System.Web.Http.Controllers.HttpActionDescriptor.get_ActionBinding() at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem..ctor(HttpControllerDescriptor controllerDescriptor) at System.Web.Http.Controllers.ApiControllerActionSelector.GetInternalSelector(HttpControllerDescriptor controllerDescriptor) at System.Web.Http.Controllers.ApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext) at System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken) at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"
}

这是我的代码集。

// WebApiConfig.cs
// Register function in WebApiConfig

public static void Register(HttpConfiguration config)
{
// Replace services by this line, to add my custom HttpControllerSelector
config.Services.Replace(typeof(IHttpControllerSelector), new CustomHttpControllerSelector(config));

// Web API routes
config.MapHttpAttributeRoutes();

config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}

// CustomeHttpControllerSelector.cs
// CustomHttpControllerSelector class to override the default one (In this case, the same controller will be returned for all request)

public class CustomHttpControllerSelector : DefaultHttpControllerSelector
{
private readonly HttpConfiguration _configuration;

public CustomHttpControllerSelector(HttpConfiguration configuration) : base(configuration)
{
_configuration = configuration;
}

public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
var ctrl = ControllerBuilder.CreateNewObject();

String controllerName = base.GetControllerName(request);
return new HttpControllerDescriptor(_configuration, controllerName, ctrl.GetType());
}
}

// ControllerBuilder.cs
// The builder to implement my custom controller. Seems the problem is here. Maybe I configured something wrong in this section?

public static class ControllerBuilder
{
public static ApiController CreateNewObject()
{
var ctrlType = CompileResultType("TmpController", new Dictionary<string, string> { { "tmpField", "System.String" } });
var ctrlObject = Activator.CreateInstance(ctrlType);

return ctrlObject as ApiController;
}

public static Type CompileResultType(string typeSignature, Dictionary<string, string> propDic)
{
TypeBuilder tb = GetTypeBuilder(typeSignature);

tb.SetParent(typeof(ApiController));

ConstructorBuilder ctor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

foreach (var item in propDic)
{
CreateProperty(tb, item.Key, Type.GetType(item.Value));
}

// For this controller, I only want a Get method to server Get request
MethodBuilder myGetMethod =
tb.DefineMethod("Get",
MethodAttributes.Public,
typeof(String), new Type[] { typeof(String) });
// Generate IL for method.
ILGenerator myMethodIL = myGetMethod.GetILGenerator();
myMethodIL.Emit(OpCodes.Ldarg_1);
myMethodIL.Emit(OpCodes.Ret);

Type objectType = tb.CreateType();
return objectType;
}

private static TypeBuilder GetTypeBuilder(string typeSignature)
{
var an = new AssemblyName(typeSignature);
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout,
null);
return tb;
}

private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
{
FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();

getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);

MethodBuilder setPropMthdBldr =
tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] { propertyType });

ILGenerator setIl = setPropMthdBldr.GetILGenerator();
Label modifyProperty = setIl.DefineLabel();
Label exitSet = setIl.DefineLabel();

setIl.MarkLabel(modifyProperty);
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, fieldBuilder);

setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);

propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
}

我尝试通过 ctrlType.GetMethod("Get").Invoke(ctrlObject, new [] {"param1"}) 在 CreateNewObject() 中调用 ctrlObject 的“Get”方法。但是,我可以获得返回字符串“param1”!!!。那么,当我请求它作为一个真正的 Web API Controller 时,我是否可以知道是什么问题导致了我在顶部引用的异常?

最佳答案

我遇到了同样的问题,我花了几个小时才弄清楚是怎么回事。

基本上,ReflectedHttpParameterDescriptor.GetCustomAttributes[TAttribute]() 试图从您正在使用的 Get(string) 方法中获取参数信息,例如参数名称和属性创造。问题是 DefineMethod() 只声明了参数类型,而不是 GetCustomAttributes() 需要的其他信息。

解决方案是在定义方法后使用 MethodBuilder.DefineParameter() 提供此信息。

// For this controller, I only want a Get method to server Get request
MethodBuilder myGetMethod = tb.DefineMethod(
name: "Get",
attributes: MethodAttributes.Public,
returnType: typeof(String),
parameterTypes: new Type[] { typeof(String) }
);

// Define parameters
myGetMethod.DefineParameter(
position: 1, // 0 is the return value, 1 is the 1st param, 2 is 2nd, etc.
attributes: ParameterAttributes.None,
strParamName: "stringParam"
);

关于c# - 在运行时创建 C# Web API 2 Controller 时出现异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47767163/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com