代码改变世界

Web API 入门 二 媒体类型

2017-11-03 14:51  糯米粥  阅读(409)  评论(0编辑  收藏  举报

 还是拿上面 那篇 Web API 入门 一  的那个来讲

在product类中加一个时间属性

初始化的时候。赋值为当前时间

 

然后通过浏览器访问,我这里是FF

 

当然。通过HttpClient也一样

 

 

你会发现。时间不是标准的时间格式,这里稍后讲。

 

这里还有一个问题。FF和后台代码。返回的数据。FF是xml格式。但HttpClient返回的是json格式

因为在HttpClient我设置了媒体格式

 

StringContent content = new StringContent(json);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

 

 

当一条HTTP消息含有一个实体时,Content-Type(内容类型)报头指定消息体的格式。这是告诉接收器如何解析消息体的内容。

WebClient这样设置

 WebClient client = new WebClient();
            //设置请求头是json格式
client.Headers.Add("Content-Type", "application/json");

当客户端发送一条请求消息时,它可能包括一个Accept报头。Accept报头是告诉服务器,客户端希望从服务器得到哪种媒体类型。例如:

Accept: text/html,application/xhtml+xml,application/xml

该报头告诉服务器,客户端希望得到的是HTML、XHTML,或XML。

 

看看刚用FF访问API时候的报头信息

 

 

WebAPI默认是xml格式,上面可以看到

对于XML、JSON,以及URL编码的表单数据,已有了内建的支持

所以。在后台请求的时候。设置了媒体类型

client.Headers.Add("Content-Type", "application/json");

 

如果通过ajax请求的时候,希望也得到json格式的。那么需要设置 Media Formatter

代码有参考网络,注意。下面的方式一,方式二都可以,但只能用一个,不能同时用,包括JsonContentNegotiator类一样

 

using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Web.Http;

namespace WebApiDemo
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );



            #region 设置API返回数据格式 方式一
            var json = config.Formatters.JsonFormatter;
            //设置格式化格式为:json格式
            json.SerializerSettings.PreserveReferencesHandling =
                Newtonsoft.Json.PreserveReferencesHandling.Objects;

            //在管道中移除xml格式,其实就是统一用json格式化
            config.Formatters.Remove(config.Formatters.XmlFormatter);

            #endregion
            #region 设置API返回数据格式 方式二
            //清空默认xml、设置json
            //GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();

            //GlobalConfiguration.Configuration.Formatters.JsonFormatter.MediaTypeMappings.Add(
            //    new QueryStringMapping("datatype", "json", "application/json"));
            #endregion

            ConfigureApi(config);    
        }

        public static void ConfigureApi(HttpConfiguration config)
        {
            var jsonFormatter = new JsonMediaTypeFormatter();
            var settings = jsonFormatter.SerializerSettings;

            IsoDateTimeConverter timeConverter = new IsoDateTimeConverter();
            //这里使用自定义日期格式
            timeConverter.DateTimeFormat = "yyyy'-'MM'-'dd' 'HH':'mm':'ss";
            settings.Converters.Add(timeConverter);


            settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            config.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator(jsonFormatter));

        }
        public class JsonContentNegotiator : IContentNegotiator
        {
            private readonly JsonMediaTypeFormatter _jsonFormatter;


            public JsonContentNegotiator(JsonMediaTypeFormatter formatter)
            {
                _jsonFormatter = formatter;
            }

            public ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
            {
                var result = new ContentNegotiationResult(_jsonFormatter, new MediaTypeHeaderValue("application/json"));
                return result;
            }
        }
    }
}

 

现在FF运行看看效果

 

发现已经是json格式了

 

 

FF发送的Accept格式是,Accept报头是告诉服务器,客户端希望从服务器得到哪种媒体类型

Accept :text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

但API后台移除了XML格式。然后指定了application/json 媒体格式

 

 

 

仔细看。你会发现。是不是时间也正常了

 

 

这是因为。也做了时间格式化

 

 

 

 

 

如果直接在Register中这样注册。也是可以的。只是要移除XML格式,然后设置为json格式

因为这是针对json的格式化器

 

  var settings = config.Formatters.JsonFormatter.SerializerSettings;
            IsoDateTimeConverter timeConverter = new IsoDateTimeConverter();
            timeConverter.DateTimeFormat = "yyyy-MM-dd HH:mm:ss";
            settings.Converters.Add(timeConverter);

            var j = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
            j.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Local;
            j.SerializerSettings.DateFormatString = "yyyy'-'MM'-'dd' 'HH':'mm':'ss";
            j.SerializerSettings.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat;

            GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new IsoDateTimeConverter
            {
                DateTimeFormat = "yyyy'-'MM'-'dd' 'HH':'mm':'ss"
            });
            config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new IsoDateTimeConverter
            {
                DateTimeFormat = "yyyy'-'MM'-'dd' 'HH':'mm':'ss"
            });

 

 

自定义媒体格式化器

就是将媒体类型格式化器添加到Web API管线,要使用HttpConfiguration对象上的Formatters属性。

演示:将Product对象序列化成一个逗号分隔的值(CSV)格式,

 

类还是上面那个prduct类

为了实现CSV格式化器,要定义一个派生于BufferedMediaTypeFormater的类:ProductCsvFormatter

 

 

既然是BufferedMediaTypeFormatter的派生类。显然要继承自:BufferedMediaTypeFormatter

 

 

ProductCsvFormatter类如下:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using System.IO;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using WebApiDemo.Models;
using System.Net.Http;

namespace WebApiDemo.Formatters
{
    /// <summary>
    /// 自定义媒体类型格式化器
    /// </summary>
    public class ProductCsvFormatter : BufferedMediaTypeFormatter
    {
        public ProductCsvFormatter()
        {
            // 添加所支持的媒体类型
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv"));
            //SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
        }

        /// <summary>
        /// 查询此 System.Net.Http.Formatting.MediaTypeFormatter 是否可以序列化指定类型的对象。
        /// </summary>
        /// <param name="type"> 要序列化的类型。</param>
        /// <returns></returns>
        public override bool CanWriteType(Type type)
        {
            /*
             格式化器可以序列化单个Product对象,以及Product对象集合。
             */
            if (type == typeof(Product))
            {
                return true;
            }
            else
            {
                Type enumerableType = typeof(IEnumerable<Product>);
                return enumerableType.IsAssignableFrom(type);
            }


            //throw new NotImplementedException();
        }

        /// <summary>
        /// 如果 System.Net.Http.Formatting.MediaTypeFormatter 可以反序列化该类型,则为 true;否则为 false。
        /// </summary>
        /// <param name="type"> 要反序列化的类型。。</param>
        /// <returns></returns>
        public override bool CanReadType(Type type)
        {
            /*
             重写CanReadType方法,以指示该格式化器可以解序列化哪种类型。
             * 在此例中,格式化器不支持解序列化,因此该方法简单地返回false。
             */
            return false;
        }

        //public override void WriteToStream(Type type, object value, Stream writeStream, System.Net.Http.HttpContent content)
        //{
        //    base.WriteToStream(type, value, writeStream, content);
        //}
        //public override void WriteToStream(Type type, object value, Stream writeStream, System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken)
        //{
        //    base.WriteToStream(type, value, writeStream, content, cancellationToken);
        //}
       
        /// <summary>
        /// 同步写入到缓冲流。
        /// 重写WriteToStream方法。通过将一种类型写成一个流,
        /// 该方法对该类型进行序列化。如果你的格式化器要支持解序列化,也可以重写ReadFromStream方法。
        /// </summary>
        /// <param name="type">要序列化的对象的类型</param>
        /// <param name="value"> 要写入的对象值。可以为 null。</param>
        /// <param name="stream"> 要写入到的流。</param>
        /// <param name="contentHeaders"> System.Net.Http.HttpContent(如果可用)。可以为 null。</param>
        public override void WriteToStream(
    Type type, object value, Stream stream, HttpContent content)
        {
            using (var writer = new StreamWriter(stream))
            {
                var products = value as IEnumerable<Product>;

                if (products != null)
                {
                    foreach (var product in products)
                    {
                        WriteItem(product, writer);
                    }
                }
                else
                {
                    var singleProduct = value as Product;
                    if (singleProduct == null)
                    {
                        throw new InvalidOperationException("Cannot serialize type");
                    }
                    WriteItem(singleProduct, writer);
                }
            }
            stream.Close();
        }
        // 将Product序列化成CSV格式的辅助器方法
        private void WriteItem(Product product, StreamWriter writer)
        {
            writer.WriteLine("{0},{1},{2},{3}", Escape(product.Id),
                Escape(product.Name), Escape(product.Category), Escape(product.Price));
        }

        static char[] _specialChars = new char[] { ',', '\n', '\r', '"' };

        private string Escape(object o)
        {
            if (o == null)
            {
                return "";
            }
            string field = o.ToString();
            if (field.IndexOfAny(_specialChars) != -1)
            {
                return String.Format("\"{0}\"", field.Replace("\"", "\"\""));
            }
            else return field;
        }
    }
}
View Code



WebApi的代码我修改了下。不用ConfigureApi(HttpConfiguration config) 方法

在WebApiConfig 中的Register方法中注册对象

config.Formatters.Add(new ProductCsvFormatter());

 

这里直接放源码

using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Web.Http;
using WebApiDemo.Formatters;

namespace WebApiDemo
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            //要使用HttpConfiguration对象上的Formatters属性。
            config.Formatters.Add(new ProductCsvFormatter());

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            //var settings = config.Formatters.JsonFormatter.SerializerSettings;
            //IsoDateTimeConverter timeConverter = new IsoDateTimeConverter();
            //timeConverter.DateTimeFormat = "yyyy-MM-dd HH:mm:ss";
            //settings.Converters.Add(timeConverter);

            //var j = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
            //j.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Local;
            //j.SerializerSettings.DateFormatString = "yyyy'-'MM'-'dd' 'HH':'mm':'ss";
            //j.SerializerSettings.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat;

            //GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new IsoDateTimeConverter
            //{
            //    DateTimeFormat = "yyyy'-'MM'-'dd' 'HH':'mm':'ss"
            //});

            config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new IsoDateTimeConverter
            {
                DateTimeFormat = "yyyy'-'MM'-'dd' 'HH':'mm':'ss"
            });

            #region 设置API返回数据格式 方式一
            //var json = config.Formatters.JsonFormatter;

            //设置格式化格式为:json格式
            /*
             * 该序列化不要。否则会出现
             * {"$id":"1","Id":1,"Name":"Tomato soup"}
             * 因为重复引用
             */
            //json.SerializerSettings.PreserveReferencesHandling =
           //    Newtonsoft.Json.PreserveReferencesHandling.Objects;

            ////在管道中移除xml格式,其实就是统一用json格式化
            //config.Formatters.Remove(config.Formatters.XmlFormatter);

            #endregion
            #region 设置API返回数据格式 方式二
            //清空默认xml、设置json
            GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();

            GlobalConfiguration.Configuration.Formatters.JsonFormatter.MediaTypeMappings.Add(
                new QueryStringMapping("datatype", "json", "application/json"));

            //GlobalConfiguration.Configuration.Formatters.JsonFormatter.MediaTypeMappings.Add(
            //   new QueryStringMapping("datatype", "text", "text/csv"));
            #endregion


            //ConfigureApi(config);
        }

        public static void ConfigureApi(HttpConfiguration config)
        {
            var jsonFormatter = new JsonMediaTypeFormatter();
            var settings = jsonFormatter.SerializerSettings;

            IsoDateTimeConverter timeConverter = new IsoDateTimeConverter();
            //这里使用自定义日期格式
            timeConverter.DateTimeFormat = "yyyy'-'MM'-'dd' 'HH':'mm':'ss";
            settings.Converters.Add(timeConverter);


            settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            config.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator(jsonFormatter));
           
            //为了将媒体类型格式化器添加到Web API管线,
            //要使用HttpConfiguration对象上的Formatters属性。
            config.Formatters.Add(new ProductCsvFormatter());

        }
        public class JsonContentNegotiator : IContentNegotiator
        {
            private readonly JsonMediaTypeFormatter _jsonFormatter;


            public JsonContentNegotiator(JsonMediaTypeFormatter formatter)
            {
                _jsonFormatter = formatter;
            }

            public ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
            {
                var result = new ContentNegotiationResult(_jsonFormatter, new MediaTypeHeaderValue("application/json"));
                return result;
            }
        }
    }
}
View Code

 

好了AP写好了。那么我们来测试下,为了测试多种情况。所有我测试自定义的text/csv外,还测试json格式

 

   static void Main(string[] args)
        {
            HttpClient client1 = new HttpClient();
            // 添加Accept报头
            client1.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/csv"));
            string result1 = client1.GetStringAsync("http://127.0.0.1:8081/api/product/getAllWeb").Result;


            WebClient client = new WebClient();
            //设置请求头是json格式
            client.Headers.Add("Content-Type", "application/json");
            byte[] responseData = null;
            //get 方式请求
            responseData = client.DownloadData("http://127.0.0.1:8081/api/product/getAllWeb");
            string result = Encoding.UTF8.GetString(responseData);

            //把结果写入csv
            System.IO.File.WriteAllText("products.csv", result1);
        }

 

最后测试可以看出。结果是正确的。根据不同的请求头。获取了不同的效果

看看 products.csv文件

 

 

 

因为在api中移除了xml格式

GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();

那我们试试用 application/json 是否可以请求到

 

Web API提供了用于JSON和XML的媒体类型格式化器。框架已默认将这些格式化器插入到消息处理管线之中。

客户端在HTTP请求的Accept报头中可以请求JSON或XML。当没有xml格式。就返回json格式

在API中。我是没有指定json格式的,所有已经判断WEB api已经集成了json格式

 

当启用xml格式的结果

 

 

 获取时间戳 时间戳转换 http://www.cnblogs.com/godbell/p/5975366.html

 var a = (long)(DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")).Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalMilliseconds);


            TimeSpan ts =  DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            var b = Convert.ToInt64(ts.TotalSeconds).ToString();

 

 

 

 

我也是根据网上的资料学习。实践。有错误的地方还请见谅。挺幸运的。站在了巨人的肩膀上

现在大部分问题前辈基本上都遇到过。都能找到答案和学习参考的资料。但那不是自己。所以自己必须得实践!!

 

 以下源码包含了本篇和上篇的内容

webAPI源码

调用webAPI源码

 

 

参考:

http://www.cnblogs.com/r01cn/p/3155653.html

http://www.cnblogs.com/r01cn/archive/2013/05/17/3083400.html