Rest API 开发分享

简介: Rest API 开发分享

概述


REST 从资源的角度来观察整个网络,分布在各处的资源由URI确定,而客户端的应用通过URI来获取资源的表示方式。获得这些表徵致使这些应用程序转变了其状态。随着不断获取资源的表示方式,客户端应用不断地在转变着其状态,所谓表述性状态转移(Representational State Transfer)。


这一观点不是凭空臆造的,而是通过观察当前Web互联网的运作方式而抽象出来的。Roy Fielding 认为,


“设计良好的网络应用表现为一系列的网页,这些网页可以看作的虚拟的状态机,用户选择这些链接导致下一网页传输到用户端展现给使用的人,而这正代表了状态的转变。”


REST是设计风格而不是标准。REST通常基于使用HTTP,URI,和XML以及HTML这些现有的广泛流行的协议和标准。


 

资源是由URI来指定。

 

对资源的操作包括获取、创建、修改和删除资源,这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。

 

通过操作资源的表现形式来操作资源。

 

资源的表现形式则是XML或者HTML,取决于读者是机器还是人,是消费web服务的客户软件还是web浏览器。当然也可以是任何其他的格式。


REST的要求


 

客户端和服务器结构

 

连接协议具有无状态性

 

能够利用Cache机制增进性能

 

层次化的系统

 

隨需代碼 - Javascript (可選)


RESTful Web 服务


RESTful Web 服务(也称为 RESTful Web API)是一个使用HTTP并遵循REST原则的Web服务。它从以下三个方面资源进行定义:URI,比如:http://example.com/resources/


§ Web服务接受与返回的互联网媒体类型,比如:JSON,XML ,YAML 等。


§ Web服务在该资源上所支持的一系列请求方法(比如:POST,GET,PUT或DELETE)。


该表列出了在实现RESTful Web 服务时HTTP请求方法的典型用途。


HTTP 请求方法在RESTful Web 服务中的典型应用


PUT 和 DELETE 方法是幂等方法。GET方法是安全方法 (不会对服务器端有修改,因此也是幂等的)。


不像基于SOAP的Web服务,RESTful Web服务并没有的“正式”标准。 这是因为REST是一种架构,而SOAP只是一个协议。虽然REST不是一个标准,但在实现RESTful Web服务时可以使用其他各种标准(比如HTTP,URL,XML,PNG等)。


REST的优点


 

可以利用缓存Cache来提高响应速度

 

通讯本身的无状态性可以让不同的服务器的处理一系列请求中的不同请求,提高服务器的扩展性

 

浏览器即可作为客户端,简化软件需求

 

相对于其他叠加在HTTP协议之上的机制,REST的软件依赖性更小

 

不需要额外的资源发现机制

 

在软件技术演进中的长期的兼容性更好


Rest 开发


首先先定义接口IRestHandler:

/// <summary>
/// The IRestHandler is an interface which provides Delete,Get,Post and Put methods.
/// </summary>
public interface IRestHandler : ICloneable
{
    /// <summary>
    /// Delete method for RestHandler
    /// </summary>
    /// <param name="processor">The rest processor.</param>
    /// <param name="authenticated">if set to <c>true</c> [authenticated].</param>
    /// <returns>The http response</returns>
    RestHandlerResponse Delete(IRestProcessor processor, bool authenticated);
    /// <summary>
    /// Get method for RestHandler
    /// </summary>
    /// <param name="processor">The rest processor.</param>
    /// <param name="authenticated">if set to <c>true</c> [authenticated].</param>
    /// <returns>The http response</returns>
    RestHandlerResponse Get(IRestProcessor processor, bool authenticated);
    /// <summary>
    /// Post method for RestHandler
    /// </summary>
    /// <param name="processor">The rest processor.</param>
    /// <param name="authenticated">if set to <c>true</c> [authenticated].</param>
    /// <returns>The http response</returns>
    RestHandlerResponse Post(IRestProcessor processor, bool authenticated);
    /// <summary>
    /// Put method for RestHandler
    /// </summary>
    /// <param name="processor">The rest processor.</param>
    /// <param name="authenticated">if set to <c>true</c> [authenticated].</param>
    /// <returns>The http response</returns>
    RestHandlerResponse Put(IRestProcessor processor, bool authenticated);
}


我们要定义一个HttpListener,先定义一个接口IRestListener:

/// <summary>
/// Listen an ip point and accept connection.
/// </summary>
public interface IRestListener : IDisposable
{
    /// <summary>
    /// Gets or sets the max allowed connections to this listener.
    /// </summary>
    int MaxConnections { get; set; }
    /// <summary>
    /// Gets or sets desktop rest manager.
    /// </summary>
    DesktopRestManager DesktopRestManager { get; set; }
    /// <summary>
    /// Gets a value that indicate if it is listening.
    /// </summary>
    bool IsRunning { get; }
    /// <summary>
    /// Gets or sets the server address information.
    /// </summary>
    IPEndPoint ServerAddress { get; set; }
    string Protocol { get; set; }
    /// <summary>
    /// Start a listener.
    /// </summary>
    /// <returns>The ip end point to listen.</returns>
    bool Start(TcpListener listener, IPEndPoint address);
    /// <summary>
    /// Stop the listener.
    /// </summary>
    /// <returns>True if successfully, else false.</returns>
    bool Stop();
}


接下来实现

public class HttpListener : IRestListener
    {
        public HttpListener(DesktopRestManager drm)
        {
            this.desktopRestManager = drm;
            this.isRunning = false;           
            MaxConnections = 50;
            this.serverAddress = new IPEndPoint(new IPAddress(new byte[] { 127, 0, 0, 1 }), 10000);
            this.Protocol = "Http";           
        }
        #region IRestServer Members
        //public event ServerStatusChangedHandler ServerStatusChanged;
        public DesktopRestManager DesktopRestManager
        {
            get { return this.desktopRestManager; }
            set { this.desktopRestManager = value; }
        }
        public bool IsRunning
        {
            get { return this.isRunning; }
        }
        public IPEndPoint ServerAddress
        {
            get { return this.serverAddress; }
            set { this.serverAddress = value; }
        }
        public int MaxConnections
        {
            get;
            set;
        }
        public string Protocol
        {
            get;
            set;
        }
        public bool Start(TcpListener tcpListener, IPEndPoint address)
        {
            this.ServerAddress = address;
            this.listener = tcpListener;
            this.isRunning = true;
            Thread th = new Thread(new ThreadStart(this.Listening));
            th.Start();
            return true;
        }
        public bool Stop()
        {
            bool success = true; ;
            if (this.isRunning == true)
            {
                try
                {
                    this.isRunning = false;
                    if (listener != null)
                    {
                        listener.Stop();
                    }
                }
                catch (SocketException socketEx)
                {
                    _traceLog.InfoFormat("Stop http rest server: {0}", socketEx.Message);
                    success = false;
                }
            }
            return success;
        }
        #endregion
        #region IDisposable Members
        public void Dispose()
        {
            this.Stop();
        }
        #endregion
        #region Private Methods
        private void Listening()
        {
            while (this.isRunning)
            {
                TcpClient tcpClient = null;
                try
                {
                    tcpClient = listener.AcceptTcpClient();
                    HttpConnection connection = new HttpConnection(tcpClient);
                    RestProcessor rh = new RestProcessor(this.desktopRestManager);
                    Thread processThread = new Thread(new ParameterizedThreadStart(req => connection.SendResponse(rh.HandleRequest(req as RestHandlerRequest))));
                    processThread.Name = "RestManager_Http_ProcessRequest";
                    processThread.Start(connection.GetRequest());
                }
                catch (SocketException socketEx)
                {
                    if (this.isRunning)
                    {
                        _traceLog.InfoFormat("Socket exception: {0}", socketEx.Message);
                    }
                    else
                    {
                        _traceLog.Info("The use stop the http listener.");
                    }
                    if (tcpClient != null && tcpClient.Connected)
                    {
                        tcpClient.Close();
                    }
                }
                catch (System.ArgumentNullException ex)
                {
                    _traceLog.ErrorFormat("Error occured: {0}", ex.Message);
                }
                catch (System.OutOfMemoryException ex)
                {
                    _traceLog.ErrorFormat("Error occured: {0}", ex.Message);
                }
                catch (System.Threading.ThreadStateException ex)
                {
                    _traceLog.ErrorFormat("Error occured: {0}", ex.Message);
                }
                catch (System.InvalidOperationException ex)
                {
                    _traceLog.ErrorFormat("Error occured: {0}", ex.Message);
                }
                catch (ApplicationException ex)
                {
                    _traceLog.ErrorFormat("Error occured: {0}", ex.Message);
                }
            }
            this.Stop();
        }
        #endregion
        #region Private Members
        private DesktopRestManager desktopRestManager;
        private bool isRunning;
        private IPEndPoint serverAddress;
        private TcpListener listener;
        private static LogManager _traceLog = new LogManager("RestManager-HttpListener");
        #endregion
    }

接下来处理HandleRequest:

/// <summary>
        /// Handles an http request for an Api call.
        /// </summary>
        public RestHandlerResponse HandleRequest(RestHandlerRequest rhr)
        {
            RestHandlerResponse res;
            // 50 Requests in maximum
            if (!this.restProcessorSemaphore.WaitOne(0))
            {
               res = new RestHandlerResponse(503);
            }
            else
            {
                try
                {
                    // There is no need decode the url here, since the address will be decoded when it is parsed.
                    //rhr.Address = System.Web.HttpUtility.UrlDecode(rhr.Address);
                    res = this.process(rhr);
                }
                catch (RestManagerException ex)
                {
                    traceLog.ErrorFormat("Error happened while processing request\n{1}.\nException info:\n{0}  ",ex.Message);
                    res = new RestHandlerResponse(500);
                }
                try
                {
                    this.restProcessorSemaphore.Release();
                }
                catch (System.Threading.SemaphoreFullException)
                {
                    traceLog.ErrorFormat("Error happened while processing Semaphore.Release");
                }
                catch (System.IO.IOException)
                {
                    traceLog.ErrorFormat("Error happened while processing Semaphore.Release");
                }
                catch (System.UnauthorizedAccessException)
                {
                    traceLog.ErrorFormat("Error happened while processing Semaphore.Release");
                }
            }
            return res;
        }


接下来我们写发送请求代码:

private JObject MakeRequest(string url)
{
    var subsequentRequest = WebRequest.Create(url) as HttpWebRequest;
    subsequentRequest.Timeout = 30000;
    subsequentRequest.Headers.Add("Authorization", "OAuth " + TestToken);
    subsequentRequest.Headers.Add("App-User", TestUserName);
    WebResponse subsequentResponse;
    try
    {
        subsequentResponse = subsequentRequest.GetResponse();
        Stream stream = subsequentResponse.GetResponseStream();
        StreamReader sr = new StreamReader(stream);
        string output = sr.ReadToEnd();
        JObject jsonStr = JObject.Parse(output);
        return jsonStr;
    }
    catch (WebException ex)
    {
        if (ex.Response != null)
        {
            HttpWebResponse errorResponse = (HttpWebResponse)ex.Response;
            StreamReader reader = new StreamReader(errorResponse.GetResponseStream());
            string output = reader.ReadToEnd();
            JObject jsonStr = JObject.Parse(output);
            return jsonStr;
        }
        else
        {
            return null;
        }
    }
}


Json的返回结果格式如下:

[{"CreatedDate":"//Date(1299687080328+0800)//","Detail":"Do Something 1","Title":"Task1"},


REST vs SOAP


成熟度:


SOAP虽然发展到现在已经脱离了初衷,但是对于异构环境服务发布和调用,以及厂商的支持都已经达到了较为成熟的情况。不同平台,开发语言之间通过SOAP来交互的web service都能够较好的互通(在部分复杂和特殊的参数和返回对象解析上,协议没有作很细致的规定,导致还是需要作部分修正)


REST国外很多大网站都发布了自己的开发API,很多都提供了SOAP和REST两种Web Service,根据调查部分网站的REST风格的使用情况要高于SOAP。但是由于REST只是一种基于Http协议实现资源操作的思想,因此各个网站的REST实现都自有一套,在后面会讲诉各个大网站的REST API的风格。也正是因为这种各自实现的情况,在性能和可用性上会大大高于SOAP发布的web service,但统一通用方面远远不及SOAP。由于这些大网站的SP往往专注于此网站的API开发,因此通用性要求不高。


由于没有类似于SOAP的权威性协议作为规范,REST实现的各种协议仅仅只能算是私有协议,当然需要遵循REST的思想,但是这样细节方面有太多没有约束的地方。REST日后的发展所走向规范也会直接影响到这部分的设计是否能够有很好的生命力。


总的来说SOAP在成熟度上优于REST。


效率和易用性:

   

SOAP协议对于消息体和消息头都有定义,同时消息头的可扩展性为各种互联网的标准提供了扩展的基础,WS-*系列就是较为成功的规范。但是也由于SOAP由于各种需求不断扩充其本身协议的内容,导致在SOAP处理方面的性能有所下降。同时在易用性方面以及学习成本上也有所增加。


   

REST被人们的重视,其实很大一方面也是因为其高效以及简洁易用的特性。这种高效一方面源于其面向资源接口设计以及操作抽象简化了开发者的不良设计,同时也最大限度的利用了Http最初的应用协议设计理念。同时,在我看来REST还有一个很吸引开发者的就是能够很好的融合当前Web2.0的很多前端技术来提高开发效率。例如很多大型网站开放的REST风格的API都会有多种返回形式,除了传统的xml作为数据承载,还有(JSON,RSS,ATOM)等形式,这对很多网站前端开发人员来说就能够很好的mashup各种资源信息。


   

因此在效率和易用性上来说,REST更胜一筹。


安全性:

   

这点其实可以放入到成熟度中,不过在当前的互联网应用和平台开发设计过程中,安全已经被提到了很高的高度,特别是作为外部接口给第三方调用,安全性可能会高过业务逻辑本身。


   

SOAP在安全方面是通过使用XML-Security和XML-Signature两个规范组成了WS-Security来实现安全控制的,当前已经得到了各个厂商的支持,.net ,php ,java 都已经对其有了很好的支持(虽然在一些细节上还是有不兼容的问题,但是互通基本上是可以的)。


   

REST没有任何规范对于安全方面作说明,同时现在开放REST风格API的网站主要分成两种,一种是自定义了安全信息封装在消息中(其实这和SOAP没有什么区别),另外一种就是靠硬件SSL来保障,但是这只能够保证点到点的安全,如果是需要多点传输的话SSL就无能为力了。安全这块其实也是一个很大的问题,今年在BEA峰会上看到有演示采用SAML2实现的网站间SSO,其实是直接采用了XML-Security和XML-Signature,效率看起来也不是很高。未来REST规范化和通用化过程中的安全是否也会采用这两种规范,是未知的,但是加入的越多,REST失去它高效性的优势越多。


应用设计与改造:

   

我们的系统要么就是已经有了那些需要被发布出去的服务,要么就是刚刚设计好的服务,但是开发人员的传统设计思想让REST的形式被接受还需要一点时间。同时在资源型数据服务接口设计上来说按照REST的思想来设计相对来说要容易一些,而对于一些复杂的服务接口来说,可能强要去按照REST的风格来设计会有些牵强。这一点其实可以看看各大网站的接口就可以知道,很多网站还要传入function的名称作为参数,这就明显已经违背了REST本身的设计思路。


   

而SOAP本身就是面向RPC来设计的,开发人员十分容易接受,所以不存在什么适应的过程。


总的来说,其实还是一个老观念,适合的才是最好的

   

技术没有好坏,只有是不是合适,一种好的技术和思想被误用了,那么就会得到反效果。REST和SOAP各自都有自己的优点,同时如果在一些场景下如果去改造REST,其实就会走向SOAP(例如安全)。


   

REST对于资源型服务接口来说很合适,同时特别适合对于效率要求很高,但是对于安全要求不高的场景。而SOAP的成熟性可以给需要提供给多开发语言的,对于安全性要求较高的接口设计带来便利。所以我觉得纯粹说什么设计模式将会占据主导地位没有什么意义,关键还是看应用场景。


   

同时很重要一点就是不要扭曲了REST现在很多网站都跟风去开发REST风格的接口,其实都是在学其形,不知其心,最后弄得不伦不类,性能上不去,安全又保证不了,徒有一个看似象摸象样的皮囊。

相关文章
|
17天前
|
API 数据安全/隐私保护 UED
探索鸿蒙的蓝牙A2DP与访问API:从学习到实现的开发之旅
在掌握了鸿蒙系统的开发基础后,我挑战了蓝牙功能的开发。通过Bluetooth A2DP和Access API,实现了蓝牙音频流传输、设备连接和权限管理。具体步骤包括:理解API作用、配置环境与权限、扫描并连接设备、实现音频流控制及动态切换设备。最终,我构建了一个简单的蓝牙音频播放器,具备设备扫描、连接、音频播放与停止、切换输出设备等功能。这次开发让我对蓝牙技术有了更深的理解,也为未来的复杂项目打下了坚实的基础。
101 58
探索鸿蒙的蓝牙A2DP与访问API:从学习到实现的开发之旅
|
7天前
|
安全 搜索推荐 数据挖掘
虾皮店铺商品API接口的开发、运用与收益
虾皮(Shopee)作为东南亚领先的电商平台,通过开放API接口为商家和开发者提供了全面的数据支持。本文详细介绍虾皮店铺商品API的开发与运用,涵盖注册认证、API文档解读、请求参数设置、签名生成、HTTP请求发送及响应解析等步骤,并提供Python代码示例。API接口广泛应用于电商导购、价格比较、商品推荐、数据分析等场景,带来提升用户体验、增加流量、提高运营效率等收益。开发者需注意API密钥安全、请求频率控制及遵守使用规则,确保接口稳定可靠。虾皮API推动了电商行业的创新与发展。
61 31
|
1天前
|
自然语言处理 搜索推荐 数据挖掘
淘宝商品描述 API 接口的开发、应用与收益
淘宝商品描述API接口的开发与应用涵盖注册成为开发者、了解API规范、选择开发工具及语言(如Python)和实现代码调用。该接口可用于优化电商平台商品展示、同步数据、竞品分析、智能客服及个性化推荐,从而提高销售转化率、降低运营成本并拓展业务机会。通过自动化处理和数据分析,企业能更精准地满足消费者需求,提升竞争力。
21 9
|
5天前
|
监控 搜索推荐 API
京东JD商品详情原数据API接口的开发、运用与收益
京东商品详情API接口是京东开放平台的重要组成部分,通过程序化方式向第三方提供商品详细信息,涵盖名称、价格、库存等。它促进了京东生态系统的建设,提升了数据利用效率,并推动了企业和商家的数字化转型。开发者可通过注册账号、获取密钥、调用接口并解析返回结果来使用该API。应用场景包括电商平台的价格监控、竞品分析、个性化推荐系统开发、移动应用开发及数据整合与共享等。该接口不仅为企业和开发者带来商业价值提升、用户体验优化,还助力数据资产积累,未来应用前景广阔。
27 9
|
9天前
|
存储 搜索推荐 API
拼多多根据ID取商品详情原数据API接口的开发、运用与收益
拼多多作为中国电商市场的重要参与者,通过开放平台提供了丰富的API接口,其中根据ID取商品详情原数据的API接口尤为重要。该接口允许开发者通过编程方式获取商品的详细信息,为电商数据分析、竞品分析、价格监测、商品推荐等多个领域带来了丰富的应用场景和显著的收益。
38 10
|
2天前
|
存储 JSON API
小红书获取笔记详情API接口的开发、应用与收益。
小红书笔记详情API采用Python与Django框架开发,使用MySQL数据库存储数据。接口通过HTTP GET请求获取笔记详情,返回JSON格式数据,包含笔记内容、作者信息、图片链接等。该API应用于小红书APP内笔记展示和互动功能,并支持第三方平台的内容整合与数据分析,提升用户体验与活跃度,促进品牌合作推广,优化平台运营效率,为平台带来显著收益。
21 1
|
8天前
|
JSON 供应链 搜索推荐
淘宝APP分类API接口:开发、运用与收益全解析
淘宝APP作为国内领先的购物平台,拥有丰富的商品资源和庞大的用户群体。分类API接口是实现商品分类管理、查询及个性化推荐的关键工具。通过开发和使用该接口,商家可以构建分类树、进行商品查询与搜索、提供个性化推荐,从而提高销售额、增加商品曝光、提升用户体验并降低运营成本。此外,它还能帮助拓展业务范围,满足用户的多样化需求,推动电商业务的发展和创新。
28 5
|
15天前
|
存储 API 计算机视觉
自学记录HarmonyOS Next Image API 13:图像处理与传输的开发实践
在完成数字版权管理(DRM)项目后,我决定挑战HarmonyOS Next的图像处理功能,学习Image API和SendableImage API。这两个API支持图像加载、编辑、存储及跨设备发送共享。我计划开发一个简单的图像编辑与发送工具,实现图像裁剪、缩放及跨设备共享功能。通过研究,我深刻体会到HarmonyOS的强大设计,未来这些功能可应用于照片编辑、媒体共享等场景。如果你对图像处理感兴趣,不妨一起探索更多高级特性,共同进步。
71 11
|
12天前
|
JSON API 开发者
Lazada 商品评论列表 API 接口:开发、应用与收益
Lazada作为东南亚领先的电商平台,其商品评论数据蕴含丰富信息。通过开发和利用Lazada商品评论列表API接口,企业可深入挖掘这些数据,优化产品、营销和服务,提升客户体验和市场竞争力。该API基于HTTP协议,支持GET、POST等方法,开发者需注册获取API密钥,并选择合适的编程语言(如Python)进行开发。应用场景包括竞品分析、客户反馈处理及精准营销,帮助企业提升销售业绩、降低运营成本并增强品牌声誉。
30 2
|
16天前
|
供应链 搜索推荐 API
1688榜单商品详细信息API接口的开发、应用与收益
1688作为全球知名的B2B电商平台,为企业提供丰富的商品信息和交易机会。为满足企业对数据的需求,1688开发了榜单商品详细信息API接口,帮助企业批量获取商品详情,应用于信息采集、校验、同步与数据分析等领域,提升运营效率、优化库存管理、精准推荐、制定市场策略、降低采购成本并提高客户满意度。该接口通过HTTP请求调用,支持多种应用场景,助力企业在电商领域实现可持续发展。
62 4

热门文章

最新文章