C# Owin 自托管服务控制器读取方法参数对象的所有 属性 值
C# Owin self hosted service controller reads all property values of the method argument objects
我正在尝试用 Asp.Net 由 Owin 自托管的 Rest WebApi 服务替换随我的应用程序发布的 WCF 服务。
我认为序列化/反序列化只是针对 DataMember 属性完成的,但它似乎对不是这样的属性做了一些事情。
我将大致解释一下问题的背景是什么:
- 服务器应用程序是一个控制台应用程序,可以运行作为一项服务。
- 每个 WCF 服务及其方法都将被具有相同方法的 Web Api 控制器所取代
- 在一段时间内,应用程序将发布两个服务端点(WCF 和 WebApi)以允许客户端使用他们选择的协议进行连接
- 每个WCF/WebApi方法只有一个参数,由特定类型的对象表示(包含所有需要的参数作为属性),并且响应始终是另一种特定类型的对象(包含结果信息作为 class 属性)。
- 每个用作请求参数或响应结果的 class 都装饰有适当的
DataContract
和 DataMember
属性
- WebApi版本使用默认serialization/deserialization (Json)
例如,WCF 服务接口是这样的:
[ServiceContract]
public interface ISessionService
{
[OperationContract]
LoginResult Login(LoginRequest request);
}
WebApi 版本控制器是这样的:
[RoutePrefix(nameof(ISessionService))]
public class SessionServiceController : ApiController
{
[HttpPost]
[Route(nameof(ISessionService.Login))]
public IHttpActionResult Login([FromBody] LoginRequest request)
{
return this.Ok(this.LoginExecute(request));
}
}
LoginExecute 方法并不重要,它只是 returns LoginResult class 的一个实例,并且仅用于 运行 服务方法逻辑。
我在服务参数的反序列化方面遇到了麻烦,因为用作参数的对象的所有属性似乎都被 Owin 管道中的某些内容读取。
我不打算深入解释为什么这会导致问题,但我可以简单地说,在某些情况下,各种 class 之间存在循环引用。
这会在读取对象实例的所有属性时导致无限循环,应该避免。
我注意到这个问题暂停了应用程序的执行,并且看到调用的堆栈跟踪正在执行 DelegatingHandler.SendAsync
方法。从这里我开始按F10一步步调试,看到读取了很多属性个getter。
我知道,要解决这个问题,我应该避免循环引用,但目前这是我无法承担的任务。
所以我想问:是否有一种解决方法可以防止 Web Api 反序列化读取反序列化参数对象的所有属性,就像 WCF 使用 DataContractSerializer 所做的那样?
编辑 24/11/2020 14:18
经过多次搜索,我找到了导致读取模型所有属性的原因:IBodyModelValidator
服务。
我找到了一个适合我的解决方案,我将用它来回答遇到这个“问题”的任何人
经过一些研究,我发现问题不是序列化,而是 IBodyModelValidator
服务,它正在读取模型的所有属性元数据,包括 属性 值。
我找到的解决方案是覆盖 HttpConfiguration.Services 中的 BodyModelValidator 服务。
这个 class 有一个名为 bool ShouldValidateType(Type type)
的方法,它可以避免对类型进行验证,并且由于我没有对模型使用任何集成验证,所以我可以从中跳过整个类型。
我使用自定义属性来确定 class 是否必须执行验证
步骤如下:
创建 WebApiOptionsAttribute class
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public sealed class WebApiOptionsAttribute : Attribute
{
public bool IsBodyModelValidatorDisabled { get; set; } = false;
public static bool GetIsBodyModelValidatorDisabled(Type type)
{
return type.GetCustomAttributes(typeof(WebApiOptionsAttribute), true).FirstOrDefault() is WebApiOptionsAttribute attribute ? attribute.IsBodyModelValidatorDisabled : false;
}
}
您可以将属性应用于任何 class 以控制是否应避免验证
创建一个 WebApiOptionsBodyModelValidator class
public class WebApiOptionsBodyModelValidator : DefaultBodyModelValidator
{
public override bool ShouldValidateType(Type type)
{
if (WebApiOptionsAttribute.GetIsBodyModelValidatorDisabled(type))
{
return false;
}
else
{
return true;
}
}
}
覆盖默认的 IBodyModelValidator 服务
在 Global.asax 中 Asp.Net:
protected void Application_Start(object sender, EventArgs e)
{
GlobalConfiguration.Configure(Register);
}
public static void Register(HttpConfiguration config)
{
config.Services.Replace(typeof(IBodyModelValidator), new WebApiOptionsBodyModelValidator());
}
或在自托管应用程序的 Owin 启动中:
public void StartWebApi()
{
this.WebService = WebApp.Start(new StartOptions(), Configuration);
}
private static void Configuration(IAppBuilder appBuilder)
{
// Configure Web API for self-host.
var config = new HttpConfiguration();
config.Services.Replace(typeof(IBodyModelValidator), new NSBodyModelValidator());
appBuilder.UseWebApi(config);
}
使用属性修饰classes
[WebApiOptions(IsBodyModelValidatorDisabled = true)]
public class LoginRequest
{
public bool Test
{
get
{
//if you place a breakpoint here, it should never hit
//if you set IsBodyModelValidatorDisabled = false, the breakpoint should hit
return true;
}
}
}
我正在尝试用 Asp.Net 由 Owin 自托管的 Rest WebApi 服务替换随我的应用程序发布的 WCF 服务。
我认为序列化/反序列化只是针对 DataMember 属性完成的,但它似乎对不是这样的属性做了一些事情。
我将大致解释一下问题的背景是什么:
- 服务器应用程序是一个控制台应用程序,可以运行作为一项服务。
- 每个 WCF 服务及其方法都将被具有相同方法的 Web Api 控制器所取代
- 在一段时间内,应用程序将发布两个服务端点(WCF 和 WebApi)以允许客户端使用他们选择的协议进行连接
- 每个WCF/WebApi方法只有一个参数,由特定类型的对象表示(包含所有需要的参数作为属性),并且响应始终是另一种特定类型的对象(包含结果信息作为 class 属性)。
- 每个用作请求参数或响应结果的 class 都装饰有适当的
DataContract
和DataMember
属性 - WebApi版本使用默认serialization/deserialization (Json)
例如,WCF 服务接口是这样的:
[ServiceContract]
public interface ISessionService
{
[OperationContract]
LoginResult Login(LoginRequest request);
}
WebApi 版本控制器是这样的:
[RoutePrefix(nameof(ISessionService))]
public class SessionServiceController : ApiController
{
[HttpPost]
[Route(nameof(ISessionService.Login))]
public IHttpActionResult Login([FromBody] LoginRequest request)
{
return this.Ok(this.LoginExecute(request));
}
}
LoginExecute 方法并不重要,它只是 returns LoginResult class 的一个实例,并且仅用于 运行 服务方法逻辑。
我在服务参数的反序列化方面遇到了麻烦,因为用作参数的对象的所有属性似乎都被 Owin 管道中的某些内容读取。
我不打算深入解释为什么这会导致问题,但我可以简单地说,在某些情况下,各种 class 之间存在循环引用。
这会在读取对象实例的所有属性时导致无限循环,应该避免。
我注意到这个问题暂停了应用程序的执行,并且看到调用的堆栈跟踪正在执行 DelegatingHandler.SendAsync
方法。从这里我开始按F10一步步调试,看到读取了很多属性个getter。
我知道,要解决这个问题,我应该避免循环引用,但目前这是我无法承担的任务。
所以我想问:是否有一种解决方法可以防止 Web Api 反序列化读取反序列化参数对象的所有属性,就像 WCF 使用 DataContractSerializer 所做的那样?
编辑 24/11/2020 14:18
经过多次搜索,我找到了导致读取模型所有属性的原因:IBodyModelValidator
服务。
我找到了一个适合我的解决方案,我将用它来回答遇到这个“问题”的任何人
经过一些研究,我发现问题不是序列化,而是 IBodyModelValidator
服务,它正在读取模型的所有属性元数据,包括 属性 值。
我找到的解决方案是覆盖 HttpConfiguration.Services 中的 BodyModelValidator 服务。
这个 class 有一个名为 bool ShouldValidateType(Type type)
的方法,它可以避免对类型进行验证,并且由于我没有对模型使用任何集成验证,所以我可以从中跳过整个类型。
我使用自定义属性来确定 class 是否必须执行验证
步骤如下:
创建 WebApiOptionsAttribute class
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public sealed class WebApiOptionsAttribute : Attribute
{
public bool IsBodyModelValidatorDisabled { get; set; } = false;
public static bool GetIsBodyModelValidatorDisabled(Type type)
{
return type.GetCustomAttributes(typeof(WebApiOptionsAttribute), true).FirstOrDefault() is WebApiOptionsAttribute attribute ? attribute.IsBodyModelValidatorDisabled : false;
}
}
您可以将属性应用于任何 class 以控制是否应避免验证
创建一个 WebApiOptionsBodyModelValidator class
public class WebApiOptionsBodyModelValidator : DefaultBodyModelValidator
{
public override bool ShouldValidateType(Type type)
{
if (WebApiOptionsAttribute.GetIsBodyModelValidatorDisabled(type))
{
return false;
}
else
{
return true;
}
}
}
覆盖默认的 IBodyModelValidator 服务
在 Global.asax 中 Asp.Net:
protected void Application_Start(object sender, EventArgs e)
{
GlobalConfiguration.Configure(Register);
}
public static void Register(HttpConfiguration config)
{
config.Services.Replace(typeof(IBodyModelValidator), new WebApiOptionsBodyModelValidator());
}
或在自托管应用程序的 Owin 启动中:
public void StartWebApi()
{
this.WebService = WebApp.Start(new StartOptions(), Configuration);
}
private static void Configuration(IAppBuilder appBuilder)
{
// Configure Web API for self-host.
var config = new HttpConfiguration();
config.Services.Replace(typeof(IBodyModelValidator), new NSBodyModelValidator());
appBuilder.UseWebApi(config);
}
使用属性修饰classes
[WebApiOptions(IsBodyModelValidatorDisabled = true)]
public class LoginRequest
{
public bool Test
{
get
{
//if you place a breakpoint here, it should never hit
//if you set IsBodyModelValidatorDisabled = false, the breakpoint should hit
return true;
}
}
}