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风格的接口,其实都是在学其形,不知其心,最后弄得不伦不类,性能上不去,安全又保证不了,徒有一个看似象摸象样的皮囊。

相关文章
|
4天前
|
监控 供应链 搜索推荐
阿里妈妈商品详情API接口:开发、应用与收益的深度剖析
阿里妈妈是阿里巴巴旗下的数字营销平台,其商品详情API接口为开发者提供了获取淘宝、天猫等电商平台商品详细信息的工具。本文介绍了该接口的开发流程、应用场景及带来的收益,揭示了其在电商生态中的重要地位。
39 6
|
4天前
|
供应链 搜索推荐 API
1688APP原数据API接口的开发、应用与收益(一篇文章全明白)
1688作为全球知名的B2B电商平台,通过开放的原数据API接口,为开发者提供了丰富的数据资源,涵盖商品信息、交易数据、店铺信息、物流信息和用户信息等。本文将深入探讨1688 APP原数据API接口的开发、应用及其带来的商业收益,包括提升流量、优化库存管理、增强用户体验等方面。
30 6
|
6天前
|
监控 搜索推荐 API
京东商品详情API接口的开发、应用与收益探索
在数字化和互联网高速发展的时代,京东通过开放商品详情API接口,为开发者、企业和商家提供了丰富的数据源和创新空间。本文将探讨该API接口的开发背景、流程、应用场景及带来的多重收益,包括促进生态系统建设、提升数据利用效率和推动数字化转型等。
25 3
|
11天前
|
供应链 搜索推荐 API
探索1688榜单商品详细信息API接口:开发、应用与收益
本文深入探讨了1688榜单商品详细信息API接口的开发与应用,涵盖接口概述、开发条件、调用方法及数据处理等内容。该API帮助企业高效获取1688平台商品信息,应用于商品信息采集、校验、同步与数据分析等领域,有效提升了企业的运营效率、库存管理、销售转化率及市场策略制定能力,降低了采购成本,提升了客户满意度。
37 9
|
14天前
|
算法 Java API
如何使用Java开发获得淘宝商品描述API接口?
本文详细介绍如何使用Java开发调用淘宝商品描述API接口,涵盖从注册淘宝开放平台账号、阅读平台规则、创建应用并申请接口权限,到安装开发工具、配置开发环境、获取访问令牌,以及具体的Java代码实现和注意事项。通过遵循这些步骤,开发者可以高效地获取商品详情、描述及图片等信息,为项目和业务增添价值。
48 10
|
14天前
|
存储 API 数据库
使用Python开发获取商品销量详情API接口
本文介绍了使用Python开发获取商品销量详情的API接口方法,涵盖API接口概述、技术选型(Flask与FastAPI)、环境准备、API接口创建及调用淘宝开放平台API等内容。通过示例代码,详细说明了如何构建和调用API,以及开发过程中需要注意的事项,如数据库连接、API权限、错误处理、安全性和性能优化等。
62 5
|
13天前
|
数据可视化 搜索推荐 API
速卖通获得aliexpress商品详情API接口的开发、应用与收益。
速卖通(AliExpress)作为阿里巴巴旗下的跨境电商平台,为全球消费者提供丰富商品。其开放平台提供的API接口支持开发者获取商品详情等信息,本文探讨了速卖通商品详情API的开发流程、应用场景及潜在收益,包括提高运营效率、降低成本、增加收入和提升竞争力等方面。
31 1
|
27天前
|
JSON API 数据格式
探索后端开发:从零构建简易RESTful API
在数字时代的浪潮中,后端开发如同搭建一座桥梁,连接着用户界面与数据世界。本文将引导读者步入后端开发的殿堂,通过构建一个简易的RESTful API,揭示其背后的逻辑与魅力。我们将从基础概念出发,逐步深入到实际操作,不仅分享代码示例,更探讨如何思考和解决问题,让每一位读者都能在后端开发的道路上迈出坚实的一步。
|
1月前
|
缓存 前端开发 API
深入浅出:后端开发中的RESTful API设计原则
【10月更文挑战第43天】在数字化浪潮中,后端开发如同搭建梦想的脚手架,而RESTful API则是连接梦想与现实的桥梁。本文将带你领略API设计的哲学之美,探索如何通过简洁明了的设计,提升开发效率与用户体验。从资源定位到接口约束,从状态转换到性能优化,我们将一步步构建高效、易用、可维护的后端服务。无论你是初涉后端的新手,还是寻求进阶的开发者,这篇文章都将为你的开发之路提供指引。让我们一起走进RESTful API的世界,解锁后端开发的新篇章。
|
1月前
|
缓存 API 数据库
Python哪个框架合适开发速卖通商品详情api?
在跨境电商平台速卖通的商品详情数据获取与整合中,Python 语言及其多种框架(如 Flask、Django、Tornado 和 FastAPI)提供了高效解决方案。Flask 简洁灵活,适合快速开发;Django 功能全面,适用于大型项目;Tornado 性能卓越,擅长处理高并发;FastAPI 结合类型提示和异步编程,开发体验优秀。选择合适的框架需综合考虑项目规模、性能要求和团队技术栈。
26 2

热门文章

最新文章