zoukankan      html  css  js  c++  java
  • 喜马拉雅第三方客户端开发(接口和接口数据解析)。

    前言:最近闲来无事,看了网上豆瓣的第三方客户端,手有点痒,决定自己动手开发一个客户端,比较了荔枝和喜马拉雅,决定开发喜马拉雅的第三方客户端。

    客户端使用了WPF开发。

    1.抓取接口;

    首先得解决接口数据的问题,使用了手机端的喜马拉雅,抓包看了接口。这里推荐使用fiddler2这个工具。从图中可以看到接口信息,包括接口地址和参数的一些数据。

    2.通过http获取接口数据和转换接口数据格式。

    这里提供一个HttpWebRequestOpt类来获取接口数据。

    using System;
    using System.Collections.Specialized;
    using System.IO;
    using System.Net;
    using System.Text;
    
    namespace XIMALAYA.PCDesktop.Untils
    {
        /// <summary>
        /// 数据操作类
        /// </summary>
        public class HttpWebRequestOpt
        {
            /// <summary>
            /// 
            /// </summary>
            public string UserAgent { get; set; }
            /// <summary>
            /// cookie
            /// </summary>
            private CookieContainer Cookies { get; set; }
    
            private HttpWebRequestOpt()
            {
                //FileVersionInfo myFileVersion = FileVersionInfo.GetVersionInfo(Path.Combine(Directory.GetCurrentDirectory(), "XIMALAYA.PCDesktop.exe"));
    
                this.Cookies = new CookieContainer();
                //this.UserAgent = string.Format("ting-ximalaya_v{0} name/ximalaya os/{1} osName/{2}", myFileVersion.FileVersion, OSInfo.Instance.OsInfo.VersionString, OSInfo.Instance.OsInfo.Platform.ToString());
                //this.Cookies.Add(new Cookie("4&_token", "935&d63fef280403904a8c0a5ee0dbe228f2d064", "/", ".ximalaya.com"));
            }
            /// <summary>
            /// 添加cookie
            /// </summary>
            /// <param name="cookie"></param>
            public void SetCookies(Cookie cookie)
            {
                this.Cookies.Add(cookie);
            }
            /// <summary>
            /// 添加cookie
            /// </summary>
            /// <param name="cookie"></param>
            public void SetCookies(string key, string val)
            {
                this.Cookies.Add(new Cookie(key, val, "/", ".ximalaya.com"));
            }
            /// <summary>
            /// 通过POST方式发送数据
            /// </summary>
            /// <param name="Url">url</param>
            /// <param name="postDataStr">Post数据</param>
            /// <returns></returns>
            public string SendDataByPost(string Url, string postDataStr)
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
    
                request.CookieContainer = this.Cookies;
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = postDataStr.Length;
                request.UserAgent = this.UserAgent;
                Stream myRequestStream = request.GetRequestStream();
                StreamWriter myStreamWriter = new StreamWriter(myRequestStream, Encoding.GetEncoding("gb2312"));
                myStreamWriter.Write(postDataStr);
                myStreamWriter.Close();
    
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Stream myResponseStream = response.GetResponseStream();
                StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
                string retString = myStreamReader.ReadToEnd();
                myStreamReader.Close();
                myResponseStream.Close();
    
                return retString;
            }
            /// <summary>
            /// 通过GET方式发送数据
            /// </summary>
            /// <param name="Url">url</param>
            /// <param name="postDataStr">GET数据</param>
            /// <returns></returns>
            public string SendDataByGET(string Url, string postDataStr)
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url + (postDataStr == "" ? "" : "?") + postDataStr);
    
                request.CookieContainer = this.Cookies;
                request.Method = "GET";
                request.ContentType = "text/html;charset=UTF-8";
                request.UserAgent = this.UserAgent;
    
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Stream myResponseStream = response.GetResponseStream();
                StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
                string retString = myStreamReader.ReadToEnd();
                myStreamReader.Close();
                myResponseStream.Close();
    
                return retString;
            }
            /// <summary>
            /// 异步通过POST方式发送数据
            /// </summary>
            /// <param name="Url">url</param>
            /// <param name="postDataStr">GET数据</param>
            /// <param name="async"></param>
            public void SendDataByPostAsyn(string Url, string postDataStr, AsyncCallback async)
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
    
                request.CookieContainer = this.Cookies;
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = postDataStr.Length;
                request.UserAgent = this.UserAgent;
                Stream myRequestStream = request.GetRequestStream();
                StreamWriter myStreamWriter = new StreamWriter(myRequestStream, Encoding.GetEncoding("gb2312"));
                myStreamWriter.Write(postDataStr);
                myStreamWriter.Close();
                myRequestStream.Close();
                request.BeginGetResponse(async, request);
            }
            /// <summary>
            /// 异步通过GET方式发送数据
            /// </summary>
            /// <param name="Url">url</param>
            /// <param name="postDataStr">GET数据</param>
            /// <param name="async"></param>
            /// <returns></returns>
            public void SendDataByGETAsyn(string Url, string postDataStr, AsyncCallback async)
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url + (postDataStr == "" ? "" : "?") + postDataStr);
    
                request.CookieContainer = this.Cookies;
                request.Method = "GET";
                request.ContentType = "text/html;charset=UTF-8";
                request.UserAgent = this.UserAgent;
    
                request.BeginGetResponse(async, request);
            }
            /// <summary>
            /// 使用HttpWebRequest POST图片等文件,带参数
            /// </summary>
            /// <param name="url"></param>
            /// <param name="file"></param>
            /// <param name="paramName"></param>
            /// <param name="contentType"></param>
            /// <param name="nvc"></param>
            /// <returns></returns>
            public string HttpUploadFile(string url, string file, string paramName, string contentType, NameValueCollection nvc)
            {
                string result = string.Empty;
                string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
                byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("
    --" + boundary + "
    ");
    
                HttpWebRequest wr = (HttpWebRequest)WebRequest.Create(url);
                wr.ContentType = "multipart/form-data; boundary=" + boundary;
                wr.Method = "POST";
                wr.KeepAlive = true;
                wr.Credentials = System.Net.CredentialCache.DefaultCredentials;
    
                Stream rs = wr.GetRequestStream();
    
                string formdataTemplate = "Content-Disposition: form-data; name="{0}"
    
    {1}";
                foreach (string key in nvc.Keys)
                {
                    rs.Write(boundarybytes, 0, boundarybytes.Length);
                    string formitem = string.Format(formdataTemplate, key, nvc[key]);
                    byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
                    rs.Write(formitembytes, 0, formitembytes.Length);
                }
                rs.Write(boundarybytes, 0, boundarybytes.Length);
    
                string headerTemplate = "Content-Disposition: form-data; name="{0}"; filename="{1}"
    Content-Type: {2}
    
    ";
                string header = string.Format(headerTemplate, paramName, file, contentType);
                byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
                rs.Write(headerbytes, 0, headerbytes.Length);
    
                FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read);
                byte[] buffer = new byte[4096];
                int bytesRead = 0;
                while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                {
                    rs.Write(buffer, 0, bytesRead);
                }
                fileStream.Close();
    
                byte[] trailer = System.Text.Encoding.ASCII.GetBytes("
    --" + boundary + "--
    ");
                rs.Write(trailer, 0, trailer.Length);
                rs.Close();
    
                WebResponse wresp = null;
                try
                {
                    wresp = wr.GetResponse();
                    Stream stream2 = wresp.GetResponseStream();
                    StreamReader reader2 = new StreamReader(stream2);
    
                    result = reader2.ReadToEnd();
                }
                catch (Exception ex)
                {
                    if (wresp != null)
                    {
                        wresp.Close();
                        wresp = null;
                    }
                }
                finally
                {
                    wr = null;
                }
    
                return result;
            }
        }
    }
    View Code

    接口地址:http://mobile.ximalaya.com/m/index_subjects;接口数据如下:

    {"ret":0,"focusImages":{"ret":0,"list":[{"id":1384,"shortTitle":"DJ张羊 谢谢你的美好(感恩特辑)","longTitle":"DJ张羊 谢谢你的美好(感恩特辑)","pic":"http://fdfs.xmcdn.com/group5/M06/A8/1D/wKgDtlR1oKHSsFngAAFlxraThWc933.jpg","type":3,"trackId":4428642,"uid":1315711},{"id":1388,"shortTitle":"小清新女神11月榜","longTitle":"小清新女神11月榜","pic":"http://fdfs.xmcdn.com/group5/M04/A8/25/wKgDtlR1owzjFmE7AAF3pxnuNxg222.jpg","type":5},{"id":1383,"shortTitle":"王朔《你也不会年轻很久》 静波播讲","longTitle":"王朔《你也不会年轻很久》 静波播讲","pic":"http://fdfs.xmcdn.com/group5/M03/A8/1C/wKgDtlR1oE-xEoq6AAEfe5PJmt4656.jpg","type":3,"trackId":4417987,"uid":12512006},{"id":1382,"shortTitle":"楚老湿大课堂(长效图-娱乐)","longTitle":"楚老湿大课堂(长效图-娱乐)","pic":"http://fdfs.xmcdn.com/group5/M06/A8/19/wKgDtlR1n7ORluWTAAFuKujnTB0163.jpg","type":3,"trackId":4422955,"uid":8401915},{"id":1365,"shortTitle":"唱响喜玛拉雅(活动图)","longTitle":"唱响喜玛拉雅(活动图)","pic":"http://fdfs.xmcdn.com/group5/M06/A5/6C/wKgDtVR0VFXA3LWXAAMruRW5vnI973.png","type":8,"url":"http://activity.ximalaya.com/activity-web/activity/57?app=iting"},{"id":1363,"shortTitle":"欧莱雅广告图24、25、27、28","longTitle":"欧莱雅广告图24、25、27、28","pic":"http://fdfs.xmcdn.com/group5/M05/A0/32/wKgDtlRyla6AnGneAAF2kpKTc2I036.jpg","type":4,"url":"http://ma8.qq.com/wap/index.html?utm_source=xmly&utm_medium=113282464&utm_term=&utm_content=xmly01&utm_campaign=CPD_LRL_MEN_MA8%20Campaign_20141118_MO_other"}]},"categories":{"ret":0,"data":[]},"latest_special":{"title":"感恩的心 感谢有你","coverPathSmall":"http://fdfs.xmcdn.com/group5/M04/AA/9B/wKgDtlR2q_jxMbU-AATUrGYasdg092_mobile_small.jpg","coverPathBig":"http://fdfs.xmcdn.com/group5/M04/AA/9B/wKgDtlR2q_jxMbU-AATUrGYasdg092.jpg","coverPathBigPlus":null,"isHot":false},"latest_activity":{"title":"唱响喜马拉雅-每年四季,打造你的音乐梦想","coverPathSmall":"http://fdfs.xmcdn.com/group5/M06/A4/DA/wKgDtlR0UQLik8xMABBJsD5tCNU868_mobile_small.jpg","isHot":true},"recommendAlbums":{"ret":0,"maxPageId":250,"count":1000,"list":[{"id":232357,"title":"今晚80后脱口秀 2014","coverSmall":"http://fdfs.xmcdn.com/group4/M01/19/5A/wKgDtFMsAq3COyRPAAUQ_GUt96k211_mobile_small.jpg","playsCounts":29318050},{"id":287570,"title":"大漠谣(风中奇缘)","coverSmall":"http://fdfs.xmcdn.com/group4/M07/7D/90/wKgDtFRGQFPzpmIsAAQ3HgQ6JRU598_mobile_small.jpg","playsCounts":669091},{"id":214706,"title":"段子来了 采采","coverSmall":"http://fdfs.xmcdn.com/group3/M04/64/9D/wKgDsVJ6DnSy_6Q7AAEXoFUKDKE679_mobile_small.jpg","playsCounts":29},{"id":233577,"title":"财经郎眼 2014","coverSmall":"http://fdfs.xmcdn.com/group2/M02/4E/2F/wKgDsFLTVG7RU3ZQAAPtxcqJYug831_mobile_small.jpg","playsCounts":8877870}]}}
    

    有了数据就需要解析数据。接口数据为JSON格式,这里使用了FluentJson这个开源项目,可以把类与JSON数据互相转换。官网上有相关的源码和实例,可以下载看一下。下面介绍使用方法。

    就针对上面的那个发现也接口我定义了一个类。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using XIMALAYA.PCDesktop.Core.Models.Album;
    using XIMALAYA.PCDesktop.Core.Models.Category;
    using XIMALAYA.PCDesktop.Core.Models.FocusImage;
    using XIMALAYA.PCDesktop.Core.Models.Subject;
    using XIMALAYA.PCDesktop.Core.Models.User;
    
    namespace XIMALAYA.PCDesktop.Core.Models.Discover
    {
        public class SuperExploreIndexResult : BaseResult
        {
            /// <summary>
            /// 焦点图
            /// </summary>
            public FocusImageResult FocusImages { get; set; }
            /// <summary>
            /// 分类
            /// </summary>
            public CategoryResult Categories { get; set; }
            /// <summary>
            /// 最后一个专题
            /// </summary>
            public object LatestSpecial { get; set; }
            /// <summary>
            /// 最后一个活动
            /// </summary>
            public object LatestActivity { get; set; }
            /// <summary>
            /// 推荐专辑
            /// </summary>
            public AlbumInfoResult1 Albums { get; set; }
    
            public SuperExploreIndexResult()
                : base()
            {
                this.doAddMap(() => this.FocusImages, "focusImages");
                this.doAddMap(() => this.Categories, "categories");
                this.doAddMap(() => this.LatestActivity, "latest_activity");
                this.doAddMap(() => this.LatestSpecial, "latest_special");
                this.doAddMap(() => this.Albums, "recommendAlbums");
            }
        }
    }
    View Code

     这个SuperExploreIndexResult类的构造函数对应了接口数据中的射影关系。

    生成的映射类如下:

    // <auto-generated>
    //     此代码由工具生成。
    //     对此文件的更改可能会导致不正确的行为,并且如果
    //     重新生成代码,这些更改将会丢失。
    //        如存在本生成代码外的新需求,请在相同命名空间下创建同名分部类实现 SuperExploreIndexResultConfigurationAppend 分部方法。
    // </auto-generated>
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    using FluentJson.Configuration;
    using FluentJson;
    using XIMALAYA.PCDesktop.Core.Data.Decorator;
    using XIMALAYA.PCDesktop.Core.Models.Discover;
    
    namespace XIMALAYA.PCDesktop.Core.Data
    {
        /// <summary>
        ///     SuperExploreIndexResult
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public partial class SuperExploreIndexResultDecorator<T> : Decorator<T>
        {
            partial void doAddOtherConfig();
            /// <summary>
            ///     
            /// </summary>
            /// <typeparam name="result"></typeparam>
            public SuperExploreIndexResultDecorator(Result<T> result)
                : base(result)
            {
    
            }
            /// <summary>
            ///     
            /// </summary>
            /// <typeparam name="result"></typeparam>
            public override void doAddConfig()
            {
                base.doAddConfig();
                this.Config.MapType<SuperExploreIndexResult>(map => map
                                        .Field<System.Int32>(field => field.Ret, type => type.To("ret"))
                        .Field<System.String>(field => field.Message, type => type.To("msg"))
                        .Field<XIMALAYA.PCDesktop.Core.Models.FocusImage.FocusImageResult>(field => field.FocusImages, type => type.To("focusImages"))
                        .Field<XIMALAYA.PCDesktop.Core.Models.Category.CategoryResult>(field => field.Categories, type => type.To("categories"))
                        .Field<System.Object>(field => field.LatestActivity, type => type.To("latest_activity"))
                        .Field<System.Object>(field => field.LatestSpecial, type => type.To("latest_special"))
                        .Field<XIMALAYA.PCDesktop.Core.Models.Album.AlbumInfoResult1>(field => field.Albums, type => type.To("recommendAlbums"))
                );
                this.doAddOtherConfig();
            }
        }
    }
    View Code

    这里只列出了一个SuperExploreIndexResult类,还有CategoryResult,FocusImageResult,AlbumInfoResult1这三个类,也做了同样的映射。这样这个接口的数据最终就可以映射为SuperExploreIndexResult类了。总之,把接口中JSON数据中的对象是全部需要隐射的。

    下面演示了如何调用上面的映射类。代码中所有带Decorator后缀的类都是映射类。采用了下装饰模式。

    using System;
    using System.ComponentModel.Composition;
    using FluentJson;
    using XIMALAYA.PCDesktop.Core.Data;
    using XIMALAYA.PCDesktop.Core.Data.Decorator;
    using XIMALAYA.PCDesktop.Core.Models.Discover;
    using XIMALAYA.PCDesktop.Core.Models.Tags;
    using XIMALAYA.PCDesktop.Untils;
    
    namespace XIMALAYA.PCDesktop.Core.Services
    {
        /// <summary>
        /// 发现页接口数据
        /// </summary>
        [Export(typeof(IExploreService))]
        class ExploreService : ServiceBase<SuperExploreIndexResult>, IExploreService
        {
            #region 属性
    
            private ServiceParams<SuperExploreIndexResult> SuperExploreIndexResult { get; set; }
    
            #endregion
    
            #region IExploreService 成员
    
            /// <summary>
            /// 获取发现首页数据
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="act"></param>
            /// <param name="param"></param>
            public void GetData<T>(Action<object> act, T param)
            {
                Result<SuperExploreIndexResult> result = new Result<SuperExploreIndexResult>();
    
                new SuperExploreIndexResultDecorator<SuperExploreIndexResult>(result);
                //分类
                new CategoryResultDecorator<SuperExploreIndexResult>(result);
                new CategoryDataDecorator<SuperExploreIndexResult>(result);
                //焦点图
                new FocusImageResultDecorator<SuperExploreIndexResult>(result);
                new FocusImageDataDecorator<SuperExploreIndexResult>(result);
                //推荐用户
                //new UserDataDecorator<SuperExploreIndexResult>(result);
                //推荐专辑
                new AlbumInfoResult1Decorator<SuperExploreIndexResult>(result);
                new AlbumData1Decorator<SuperExploreIndexResult>(result);
                //专题列表
                //new SubjectListResultDecorator<SuperExploreIndexResult>(result);
                //new SubjectDataDecorator<SuperExploreIndexResult>(result);
    
                this.SuperExploreIndexResult = new ServiceParams<SuperExploreIndexResult>(Json.DecoderFor<SuperExploreIndexResult>(config => config.DeriveFrom(result.Config)), act);
                //this.Act = act;
                //this.Decoder = Json.DecoderFor<SuperExploreIndexResult>(config => config.DeriveFrom(result.Config));
    
                try
                {
                    this.Responsitory.Fetch(WellKnownUrl.SuperExploreIndex, param.ToString(), asyncResult =>
                    {
                        this.GetDecodeData<SuperExploreIndexResult>(this.GetDataCallBack(asyncResult), this.SuperExploreIndexResult);
                    });
                }
                catch (Exception ex)
                {
                    this.SuperExploreIndexResult.Act.BeginInvoke(new SuperExploreIndexResult
                    {
                        Ret = 500,
                        Message = ex.Message
                    }, null, null);
                }
            }
    
            #endregion
        }
    }
    View Code

    如上,只要配置好映射关系,通过T4模板我们可以生成对应的映射关系类。

    附上源码

    下篇,客户端使用了prism+mef这个框架,单独开发模块,最后组合的方式。未完待续。。。。

  • 相关阅读:
    线程

    线程
    【java】彩票中奖码生成器:java.util.Random里的方法public int nextInt(int bound)
    【java】java.lang.Math:public static long round(double a)和public static int round(float a)
    【java】对象克隆protected Object clone() throws CloneNotSupportedException
    【java】对象变成垃圾被垃圾回收器gc收回前执行的操作:Object类的protected void finalize() throws Throwable
    【java】计算一段代码执行时长java.lang.System类里的public static long currentTimeMillis()方法
    【java】多线程同步生产者消费者问题
    【java】多线程同步死锁
  • 原文地址:https://www.cnblogs.com/nicktyui/p/4126698.html
Copyright © 2011-2022 走看看