ASP.Net Webmethod: как десериализовать параметр с классом наследования? - PullRequest
0 голосов
/ 14 декабря 2018

У меня есть 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 () , возникает исключение.

Пожалуйста, скажите мне, что мне здесь не хватает:)

1 Ответ

0 голосов
/ 17 декабря 2018

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); }
        }
    }
}
...