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

客户端使用了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("\r\n--" + boundary + "\r\n");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}\"\r\n\r\n{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}\"\r\nContent-Type: {2}\r\n\r\n";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("\r\n--" + boundary + "--\r\n");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这个框架,单独开发模块,最后组合的方式。未完待续。。。。

转载于:https://www.cnblogs.com/nicktyui/p/4126698.html

喜马拉雅第三方客户端开发(接口和接口数据解析)。相关推荐

  1. 喜马拉雅第三方客户端开发(界面)。

    上一篇说了如何提取接口数据.这里讲下界面用到的一些库. 今天给大家推荐metro扁平化样式UI控件库:MahApps.Metro . 官方地址:http://mahapps.com/guides/qu ...

  2. Java调用接口获取json数据解析后保存到数据库

    文章目录 1.在yml文件中配置自己定义的接口URL 2.在Controller中添加请求方法和路径 3.在Service中添加方法 4.在ServiceImpl中实现方法 今天给大家带来一个调用接口 ...

  3. Winform中怎样使用HttpClient调用http的get和post接口并将接口返回json数据解析为实体类

    场景 需要在Winform的页面初始化之后,调用http的接口,并将返回的json数据 解析,赋值到窗体中的TextBox组件中. 使用接口测试工具测试的接口返回数据如下 注: 博客: 霸道流氓气质的 ...

  4. java获取微信xml数据格式的文件_(5)微信二次开发 之 XML格式数据解析

    1.首先理解一下html html的全名是:HyperText Transfer markup language 超级文本标记语言,html本质上是一门标记(符合)语言,在html里,这些标记是事先定 ...

  5. Ace教你一步一步做Android新闻客户端(三) JSON数据解析

    对于服务器端来说,返回给客户端的数据格式一般分为html.xml和json这三种格式,现在给大家讲解一下json这个知识点, 1 如何通过json-lib和gson这两个json解析库来对解析我们的j ...

  6. 服务器返回文件格式,iOS开发--服务器返回的数据解析

    App要与服务器交互才能达到数据更新和获取资源 那么: 服务器返回客户端的数据,一般返回两种格式:JSON格式.XML格式 (文件下载除外) 什么是JSON 轻量级数据格式,一般用于数据交互 JSON ...

  7. spark中读取json_【spark】文件读写和JSON数据解析

    1.读文件 通过 sc.textFile("file://")方法来读取文件到rdd中. val lines = sc.textFile("file://")/ ...

  8. 【Lilishop商城】No4-1.业务逻辑的代码开发,涉及到:会员B端第三方登录使用及后端接口(微信、QQ等)

    仅涉及后端,全部目录看顶部专栏,代码.文档.接口路径在: [Lilishop商城]记录一下B2B2C商城系统学习笔记~_清晨敲代码的博客-CSDN博客 全篇会结合业务介绍重点设计逻辑,其中重点包括接口 ...

  9. Tencent APIJSON 零代码、全功能、强安全 ORM 库 后端接口和文档零代码,前端(客户端) 定制返回 JSON 的数据和结构

    项目介绍 零代码.全功能.强安全 ORM 库 后端接口和文档零代码,前端(客户端) 定制返回 JSON 的数据和结构. A JSON Transmission Protocol and an ORM ...

最新文章

  1. linux动态二进制翻译,仿真:解释和二进制翻译
  2. Java JPanel的使用
  3. Nginx自动安装脚本
  4. 5分钟盗走你的隐私照片,这个全球性漏洞到底有多可怕
  5. mysql.sock 111,错误2002(HY000):无法通过套接字’/var/run/mysqld/mysqld.sock’连接到本地MySQL服务器(111)...
  6. java 线程 john_教你Java开发中多线程同步技巧
  7. windows10完全删除mysql_Windows 10系统下彻底删除卸载MySQL的方法教程
  8. eclipseini设置使用的jdk_设置 Maven 对应的JDK 版本
  9. 校园网规划与设计(附加完整word文档和pkt文件)
  10. 阿里云对象存储OSS简介和使用
  11. 计算机导论知识点总结(下)
  12. 这可能是最全最好的BLAST教程
  13. Arduino火焰传感器(红外线接收器)的使用
  14. 童甫带领下的海航科技集团的无人运输船正式亮相引关注
  15. 图像处理_ISP_坏点矫正
  16. 2021-2027全球与中国可待因止疼药市场现状及未来发展趋势
  17. 中图杯获奖作品计算机组,高教杯”全国大学生先进成图技术与产品信息建模创新大赛试题资料.doc...
  18. 如何用linux指令集,linux – 如何知道我是否可以使用FMA指令集进行编译?
  19. setup timing violation and hold timing violation in same path
  20. Camera Calibration

热门文章

  1. 软件测试进阶之路_心得体会
  2. 有谁知道pos机刷卡费率多少?
  3. 计算机在职双证博士的学校,计划不好做?!那是你没搞明白在职双证博士的整体流程...
  4. 第二十三天-邮件自动发送
  5. 物料主数据经典案例 | 让每一个量具都发挥价值
  6. 南华大学医院信息系统——门诊管理系统软件工程文档
  7. mysql5.5太平洋,太平洋软件下载站SQL全功能新云2.1商业版代码 下载
  8. 编程简单的计算机,计算机简单编程示例.doc
  9. 百度埋点 android,数据分析之关于APP埋点的一切
  10. linux展现文件夹目录