ASP.Net Webmethod:如何使用继承反序列化参数 class?
ASP.Net Webmethod: how to deserialize parameter with inheritance class?
我在标有 [ScriptService] 的 class 中定义了一个 WebMethod,如下所示:
[WebMethod(enableSession: false), ScriptMethod(UseHttpGet = false, ResponseFormat = ResponseFormat.Json)]
public static JsonResult ReportApplicationActivity(JsonApplicationActivityReport request, bool isTestUpload)
{ ... }
JsonApplicationActivityReport 类型的参数是一个抽象基础 class,可能是许多(级别 1)子 class 之一。
问题:当我从 WPF 客户端调用此 WebMethod 时,该方法永远不会被命中,并且会在客户端抛出异常,说明类似 "Internal server error (500)" 的内容。
我在Global.asax中的配置:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.SerializationBinder = new JsonApiUtils.InheritanceSerializationBinder();
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
所以我试图告诉 JSON 序列化程序在我解析继承的地方使用自定义 SerializationBinder。它看起来像这样:
public class InheritanceSerializationBinder : DefaultSerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
switch (typeName)
{
case "JsonMeasurementActivityReport[]": return typeof(JsonMeasurementActivityReport[]);
case "JsonMeasurementActivityReport": return typeof(JsonMeasurementActivityReport);
case "JsonSimulationActivityReport[]": return typeof(JsonSimulationActivityReport[]);
case "JsonSimulationActivityReport": return typeof(JsonSimulationActivityReport);
default: return base.BindToType(assemblyName, typeName);
}
}
}
但是 BindToType() 方法在请求期间从未被调用。
我还在 Web.config 中打开了跟踪,并且可以看到请求(每次调用 2 个,无论出于何种原因......)到达服务器,但 /[= 中没有提供任何错误消息或任何对我有帮助的信息41=].
最后,这就是我的客户端代码的样子:
JsonApplicationActivityReport request = new JsonApplicationActivityReport () {...};
HttpWebRequest webRequest = GetWebRequest(Endpoints.ReportApplicationActivity);
using (var writer = webRequest.GetRequestStream())
{
string jsonRequest = JsonConvert.SerializeObject(new { request = request, isTestUpload = isTestUpload }, Formatting.Indented, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); //also includes type information when serializing to JSON (required because the server expects an abstract class that has to be deserialized into the actual child activity class)
byte[] requestData = Encoding.UTF8.GetBytes(jsonRequest);
writer.Write(requestData, 0, requestData.Length);
}
var webResponse = (HttpWebResponse)webRequest.GetResponse();
var responseStream = new StreamReader(webResponse.GetResponseStream());
一旦调用 GetResponse(),就会触发异常。
请告诉我这里缺少什么:)
WebService
/ScriptService
不遵守您在 Global.asax
中设置的序列化程序设置。它使用硬编码到 WebServiceData
中的 System.Web.Script.Serialization.JavaScriptSerializer
,您对此没有太多控制权。
但是,它允许您注册继承自 JavaScriptConverter
的自定义 JavaScript 转换器。为此,您需要在 Web.config
文件中使用 system.web.extensions/scripting/webServices/jsonSerialization/converters
配置。
例如,假设您实施了 JsonApplicationActivityReportConverter
来处理 JsonApplicationActivityReport
class 的反序列化。在这种情况下,配置如下所示
<configuration>
<!-- ... -->
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization>
<converters>
<add name="JsonApplicationActivityReportConverter" type="YourApplicationNamespace.JsonApplicationActivityReportConverter, YourApplicationAssemblyName"/>
</converters>
</jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>
</configuration>
然而,缺点是您必须重新发明大部分反序列化才能在 JsonApplicationActivityReportConverter
中正确处理此问题。作为起点,class 看起来像...
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Web.Script.Serialization;
namespace YourApplicationNamespace {
public class JsonApplicationActivityReportConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
var typeName = (string) dictionary["$type"];
var typeInfo = Type.GetType(typeName);
var result = Activator.CreateInstance(typeInfo);
foreach (var property in typeInfo.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
if (property.CanWrite && dictionary.ContainsKey(property.Name))
{
property.SetValue(result, dictionary[property.Name]);
}
}
return result;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
// Reverse the process here if the type is to be used the other direction
}
public override IEnumerable<Type> SupportedTypes
{
get { yield return typeof(JsonApplicationActivityReport); }
}
}
}
我在标有 [ScriptService] 的 class 中定义了一个 WebMethod,如下所示:
[WebMethod(enableSession: false), ScriptMethod(UseHttpGet = false, ResponseFormat = ResponseFormat.Json)]
public static JsonResult ReportApplicationActivity(JsonApplicationActivityReport request, bool isTestUpload)
{ ... }
JsonApplicationActivityReport 类型的参数是一个抽象基础 class,可能是许多(级别 1)子 class 之一。 问题:当我从 WPF 客户端调用此 WebMethod 时,该方法永远不会被命中,并且会在客户端抛出异常,说明类似 "Internal server error (500)" 的内容。
我在Global.asax中的配置:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.SerializationBinder = new JsonApiUtils.InheritanceSerializationBinder();
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
所以我试图告诉 JSON 序列化程序在我解析继承的地方使用自定义 SerializationBinder。它看起来像这样:
public class InheritanceSerializationBinder : DefaultSerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
switch (typeName)
{
case "JsonMeasurementActivityReport[]": return typeof(JsonMeasurementActivityReport[]);
case "JsonMeasurementActivityReport": return typeof(JsonMeasurementActivityReport);
case "JsonSimulationActivityReport[]": return typeof(JsonSimulationActivityReport[]);
case "JsonSimulationActivityReport": return typeof(JsonSimulationActivityReport);
default: return base.BindToType(assemblyName, typeName);
}
}
}
但是 BindToType() 方法在请求期间从未被调用。 我还在 Web.config 中打开了跟踪,并且可以看到请求(每次调用 2 个,无论出于何种原因......)到达服务器,但 /[= 中没有提供任何错误消息或任何对我有帮助的信息41=].
最后,这就是我的客户端代码的样子:
JsonApplicationActivityReport request = new JsonApplicationActivityReport () {...};
HttpWebRequest webRequest = GetWebRequest(Endpoints.ReportApplicationActivity);
using (var writer = webRequest.GetRequestStream())
{
string jsonRequest = JsonConvert.SerializeObject(new { request = request, isTestUpload = isTestUpload }, Formatting.Indented, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); //also includes type information when serializing to JSON (required because the server expects an abstract class that has to be deserialized into the actual child activity class)
byte[] requestData = Encoding.UTF8.GetBytes(jsonRequest);
writer.Write(requestData, 0, requestData.Length);
}
var webResponse = (HttpWebResponse)webRequest.GetResponse();
var responseStream = new StreamReader(webResponse.GetResponseStream());
一旦调用 GetResponse(),就会触发异常。
请告诉我这里缺少什么:)
WebService
/ScriptService
不遵守您在 Global.asax
中设置的序列化程序设置。它使用硬编码到 WebServiceData
中的 System.Web.Script.Serialization.JavaScriptSerializer
,您对此没有太多控制权。
但是,它允许您注册继承自 JavaScriptConverter
的自定义 JavaScript 转换器。为此,您需要在 Web.config
文件中使用 system.web.extensions/scripting/webServices/jsonSerialization/converters
配置。
例如,假设您实施了 JsonApplicationActivityReportConverter
来处理 JsonApplicationActivityReport
class 的反序列化。在这种情况下,配置如下所示
<configuration>
<!-- ... -->
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization>
<converters>
<add name="JsonApplicationActivityReportConverter" type="YourApplicationNamespace.JsonApplicationActivityReportConverter, YourApplicationAssemblyName"/>
</converters>
</jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>
</configuration>
然而,缺点是您必须重新发明大部分反序列化才能在 JsonApplicationActivityReportConverter
中正确处理此问题。作为起点,class 看起来像...
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Web.Script.Serialization;
namespace YourApplicationNamespace {
public class JsonApplicationActivityReportConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
var typeName = (string) dictionary["$type"];
var typeInfo = Type.GetType(typeName);
var result = Activator.CreateInstance(typeInfo);
foreach (var property in typeInfo.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
if (property.CanWrite && dictionary.ContainsKey(property.Name))
{
property.SetValue(result, dictionary[property.Name]);
}
}
return result;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
// Reverse the process here if the type is to be used the other direction
}
public override IEnumerable<Type> SupportedTypes
{
get { yield return typeof(JsonApplicationActivityReport); }
}
}
}