3

我收到了这条 ServiceStack 小消息:

[Route("/server/time", "GET")]
public class ServerTime : IReturn<ServerTime>
{
    public DateTimeOffset DateTime { get; set; }
    public TimeZoneInfo TimeZone { get; set; }
}

其各自的服务处理程序如下:

public object Get(ServerTime request)
{
    return new ServerTime
    {
        DateTime = DateTimeOffset.Now,
        TimeZone = TimeZoneInfo.Local,
    };
}

客户端测试代码如下所示:

var client = new JsonServiceClient("http://localhost:54146/");
var response = client.Get<ServerTime>(new ServerTime());

但是 response.TimeZoneInfo 总是空的......

服务的元数据(JSON)也没有显示它:

(JSON 元数据页面中的示例请求)

POST /json/reply/ServerTime HTTP/1.1 
Host: localhost 
Content-Type: application/json
Content-Length: length

{"DateTime":"\/Date(-62135596800000)\/"}

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: length

{"DateTime":"\/Date(-62135596800000)\/"}

另一方面,XML 和 CSV 格式似乎可以正确处理它:

POST /xml/reply/ServerTime HTTP/1.1 
Host: localhost 
Content-Type: application/xml
Content-Length: length

<ServerTime xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/PtsSampleService.ServiceModel">
  <DateTime xmlns:d2p1="http://schemas.datacontract.org/2004/07/System">
    <d2p1:DateTime>0001-01-01T00:00:00Z</d2p1:DateTime>
    <d2p1:OffsetMinutes>0</d2p1:OffsetMinutes>
  </DateTime>
  <TimeZone xmlns:d2p1="http://schemas.datacontract.org/2004/07/System" i:nil="true" />
</ServerTime>

HTTP/1.1 200 OK
Content-Type: application/xml
Content-Length: length

<ServerTime xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/PtsSampleService.ServiceModel">
  <DateTime xmlns:d2p1="http://schemas.datacontract.org/2004/07/System">
    <d2p1:DateTime>0001-01-01T00:00:00Z</d2p1:DateTime>
    <d2p1:OffsetMinutes>0</d2p1:OffsetMinutes>
  </DateTime>
  <TimeZone xmlns:d2p1="http://schemas.datacontract.org/2004/07/System" i:nil="true" />
</ServerTime>

为什么我要问这个而不是使用“XML 客户端”?

这都是关于一致性的。如果 API 在所有可能的客户端上不一致,那么就不能依赖它!我将不得不删除 JSON 格式化程序(我不能这样做,因为我想在 JavaScript 中使用它)或者我必须单独拆分许多 TimeZoneInfo 字段......或者找到一种方法来制作 JSON 序列化器处理它!

而且,事实上,XML 也不起作用。这XmlServiceClient给了我这个错误:

{“第 1 行位置 290 错误。元素 ':AdjustmentRules' 包含映射到名称 ' http://schemas.datacontract.org/2004/07/System:ArrayOfTimeZoneInfo.AdjustmentRule ' 的类型的数据。反序列化器没有知道映射到此名称的任何类型。考虑使用 DataContractResolver 或将与“ArrayOfTimeZoneInfo.AdjustmentRule”对应的类型添加到已知类型列表中 - 例如,通过使用 KnownTypeAttribute 属性或将其添加到已知类型列表中传递给 DataContractSerializer。"}

有谁知道为什么默认情况下不处理它?

4

2 回答 2

2

TimeZoneInfo序列化为XML存在一个已知问题:请参阅有关在 WCF 中序列化对象的问题链接的 MSDN 讨论与解决方法。不确定该解决方法是否适用于 ServiceStack。

但我建议通过只发送Id时区的或其他一些简单的标识符来完全避免这个问题。

于 2013-06-22T13:15:35.133 回答
2

似乎没有一种优雅的方式可以传递TimeZoneInfo给客户端,所以我只是为它创建了一个 DTO,不出所料地命名为TimeZoneInformation

[Serializable]
public class TimeZoneInformation
{
    public string Id { get; set; }
    public TimeSpan BaseUtcOffset { get; set; }
    public string DisplayName { get; set; }
    public string DaylightName { get; set; }
    public string StandardName { get; set; }
    public bool SupportsDST { get; set; }
    public static implicit operator TimeZoneInformation(TimeZoneInfo source)
    {
        return new TimeZoneInformation
        {
            Id = source.Id,
            DisplayName = source.DisplayName,
            BaseUtcOffset = source.BaseUtcOffset,
            DaylightName = source.DaylightName,
            StandardName = source.StandardName,
            SupportsDST = source.SupportsDaylightSavingTime,
        };
    }
    public static implicit operator TimeZoneInfo(TimeZoneInformation source)
    {
        return TimeZoneInfo.FindSystemTimeZoneById(source.Id);
    }
}

我的服务方法不是这样的:

public object Get(ServerTime request)
{
    return new ServerTime
    {
        DateTime = DateTimeOffset.Now,
        TimeZoneInfo = TimeZoneInfo.Local,
    };
}

通话的元数据是:

POST /json/reply/ServerTime HTTP/1.1 
Host: localhost 
Content-Type: application/json
Content-Length: length

{"DateTime":"\/Date(-62135596800000)\/","TimeZoneInfo":{"Id":"String","BaseUtcOffset":"PT0S","DisplayName":"String","DaylightName":"String","StandardName":"String","SupportsDST":false}}

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: length

{"DateTime":"\/Date(-62135596800000)\/","TimeZoneInfo":{"Id":"String","BaseUtcOffset":"PT0S","DisplayName":"String","DaylightName":"String","StandardName":"String","SupportsDST":false}}

对该方法的实际调用会返回此 JSON:

{"DateTime":"\/Date(1371925432883-0300)\/","TimeZoneInfo":{"Id":"E. South America Standard Time","BaseUtcOffset":"-PT3H","DisplayName":"(UTC-03:00) Brasília","DaylightName":"Horário brasileiro de verão","StandardName":"Hora oficial do Brasil","SupportsDST":true}}

和 XML:

<ServerTime><DateTime><d2p1:DateTime>2013-06-22T21:24:22.2741641Z</d2p1:DateTime><d2p1:OffsetMinutes>-180</d2p1:OffsetMinutes></DateTime><TimeZoneInfo><_x003C_BaseUtcOffset_x003E_k__BackingField>-PT3H</_x003C_BaseUtcOffset_x003E_k__BackingField><_x003C_DaylightName_x003E_k__BackingField>Horário brasileiro de verão</_x003C_DaylightName_x003E_k__BackingField><_x003C_DisplayName_x003E_k__BackingField>(UTC-03:00) Brasília</_x003C_DisplayName_x003E_k__BackingField><_x003C_Id_x003E_k__BackingField>E. South America Standard Time</_x003C_Id_x003E_k__BackingField><_x003C_StandardName_x003E_k__BackingField>Hora oficial do Brasil</_x003C_StandardName_x003E_k__BackingField><_x003C_SupportsDST_x003E_k__BackingField>true</_x003C_SupportsDST_x003E_k__BackingField></TimeZoneInfo></ServerTime>

所有类型化的客户端都完美地反序列化它。

我不是 100% 快乐,但尽可能对此感到快乐......

有人可能会问我为什么不只发送 ID。时区 ID 特定于 windows。由于我必须与不同的客户交谈,因此我无法发送 ID,希望他们能够相应地重建服务器的时区数据。我已经在返回给客户端的 DateTimeOffset 上发送了 OFFSET,但在某些情况下还不够,因为仅偏移量不足以确定时区或夏令时是否生效。因此,发送客户端正确解释服务器日期和时间所需的所有内容是此特定应用程序的最佳解决方案。

于 2013-06-22T21:29:49.563 回答