WCF使用WebHttpBinding绑定像ASP.NET WebApi一样返回Json格式数据

所以为什么不用ASP.NET WebApi呢(这样就不需要IIS了(

流程上是先建一个WCF服务项目,然后用WCF配置工具打开App.config。在高级-终结点行为中新建一个配置,比如说起名为restfulBehavior,然后点下面的添加,选中webHttp,添加(当然也可以修改这个子节点的属性配置,比如说修改DefaultOutgoingResponseFormat为Json)。在服务-终结点里把Binging设置从原来默认的basicHttpBing改为webHttpBing,修改BehaviorConfiguration为刚才的restfulBehavior。

这样就完成了基本配置修改。然后再在代码层修改。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WcfServiceLibrary1
{
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        [WebGet(UriTemplate = "Data", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        FooObj GetData();

        [OperationContract]
        [WebGet(UriTemplate = "NullData", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        FooObj GetNullData();

        [OperationContract]
        [WebGet(UriTemplate = "Add?a={a}&b={b}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        decimal Add(decimal a, decimal b);

        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "Data", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        bool SetData(FooObj foo);
    }

    [DataContract]
    public class FooObj
    {
        [DataMember]
        public string FooStr { get; set; }

        [DataMember]
        public DateTime FooTime { get; set; }
    }

}

这里的WebInvoke感觉就相当于ASP.NET中的[HttpGet] + [Route]。

关于返回值,其实也和ASP.NET一样,微软爸爸会自动帮我们把FooObj序列化成Json返回,然而基本类型就直接返回ToString()了。

//FooObj ret = new FooObj() { FooStr = "", FooTime = DateTime.Now };
{
    "FooStr": "",
    "FooTime": "/Date(1524108062758+0800)/"
}

//FooObj ret = null


//int ret = 2
2

//bool ret = true
true

//string ret = "123"
123

//string ret = null

这当中我们可以观察发现Datetime类型序列化成Json后变成”/Date(1524108062758+0800)/”了,这个到前端可能就需要

var datestr = "/Date(1524108062758+0800)/";
var date = eval("new " + datestr.slice(1, -1));

当然问题的关键不在于前端转换这里,而在于序列化这里。这里有一个超巨大的坑点,这个时间包含了时区信息,假如这个datetime没有被赋值,那他就是一个默认值[0001/01/01 00:00:00],这个值在转为UNIX时间戳时会爆炸(负溢出)。

假如我们把时间设定在1970/1/1,那输出将会得到”/Date(-28800000+0800)/”。这样我们可以发现这是因为东八区+8:00引起的UTC时间减少8小时导致的负溢出。

发现原因后我们就可以这样解决这个问题。

DataContract]
public class FooObj
{
    [DataMember]
    public string FooStr { get; set; }

    [DataMember]
    public DateTime FooTime { get; set; }
    
    [OnSerializing]
    internal void OnSerializingMethod(StreamingContext context)
    {
        var propertys = this.GetType().GetProperties();

        foreach (var pi in propertys.Where(e => e.PropertyType == typeof(DateTime)))
        {
            DateTime obj = (DateTime)pi.GetValue(this, null);
            pi.SetValue(this, obj.ToUniversalTime(), null);
        }
    }
}

添加OnSerializingMethod方法在序列化前把所有Datetime类型的字段值全部去除时区。这样0001/1/1这个日期将会得到”/Date(-62135596800000)/”。

大致就是这样了。

参考:

WCF Web HTTP 编程模型

创建不使用 ASP.NET 的 WCF AJAX 服务

其他:

JSONP

缓存(怎么又变成ASP.NET了呢)

WCF Web HTTP 错误处理(当然大多数情况下是try{}catch{}自定义的)

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注