У меня есть WebMethod, определенный в классе, помеченном [ScriptService] следующим образом:
[WebMethod(enableSession: false), ScriptMethod(UseHttpGet = false, ResponseFormat = ResponseFormat.Json)]
public static JsonResult ReportApplicationActivity(JsonApplicationActivityReport request, bool isTestUpload)
{ ... }
Параметр типа JsonApplicationActivityReport является абстрактным базовым классом и может быть одним из многих (уровень 1) дочерних классов. Проблема: когда я вызываю этот WebMethod из клиента WPF, метод никогда не срабатывает, и на стороне клиента выдается исключение с сообщением вроде «Внутренняя ошибка сервера (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 на вызов по любой причине ...) поступают на сервер, но в /trace.axd нет сообщений об ошибках или чего-либо, что может мне помочь.
Наконец, вот как выглядит мой клиентский код:
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. Он использует System.Web.Script.Serialization.JavaScriptSerializer, который жестко запрограммирован в WebServiceData, и у вас нет особого контроля над этим.
Однако он позволяет вам регистрировать собственные конвертеры JavaScript, унаследованные от JavaScriptConverter. Для этого вам необходимо использовать конфигурацию system.web.extensions/scripting/webServices/jsonSerialization/converters в вашем файле Web.config.
Например, предположим, что у вас реализован JsonApplicationActivityReportConverter для обработки десериализации класса JsonApplicationActivityReport. В этом случае конфигурация будет выглядеть следующим образом
<configuration>
<!-- ... -->
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization>
<converters>
<add name = "JsonApplicationActivityReportConverter" type = "YourApplicationNamespace.JsonApplicationActivityReportConverter, YourApplicationAssemblyName"/>
</converters>
</jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>
</configuration>
Однако обратная сторона заключается в том, что вам придется заново изобретать большую часть десериализации, чтобы правильно с этим справиться в вашем JsonApplicationActivityReportConverter. В качестве отправной точки класс будет выглядеть примерно так ...
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); }
}
}
}
Спасибо, этот ответ отличный и решил мою проблему. Очень грустно видеть, что не существует программного способа регистрации пользовательского JavaScriptConverter, а необходимость реализации логики десериализации несколько неудобна. Жаль, что они не использовали JSON.Net для WebMethods ...