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

相关文章
|
1月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
100 3
|
3天前
|
API 开发工具 数据库
开发一份API接口,需要注意这些,看你做到了几项
本文介绍了设计API接口时需注意的关键点,包括数字签名、敏感数据加密与脱敏、限流、参数校验、统一返回与异常处理、请求日志记录、幂等设计、数据量限制、异步处理、参数定义、完整文档及开发者对接SDK等内容,旨在帮助开发者设计出安全、稳定、易维护的API接口。
36 6
开发一份API接口,需要注意这些,看你做到了几项
|
11天前
|
JSON 安全 API
如何使用Python开发API接口?
在现代软件开发中,API(应用程序编程接口)用于不同软件组件之间的通信和数据交换,实现系统互操作性。Python因其简单易用和强大功能,成为开发API的热门选择。本文详细介绍了Python开发API的基础知识、优势、实现方式(如Flask和Django框架)、实战示例及注意事项,帮助读者掌握高效、安全的API开发技巧。
37 3
如何使用Python开发API接口?
|
7天前
|
存储 SQL API
探索后端开发:构建高效API与数据库交互
【10月更文挑战第36天】在数字化时代,后端开发是连接用户界面和数据存储的桥梁。本文深入探讨如何设计高效的API以及如何实现API与数据库之间的无缝交互,确保数据的一致性和高性能。我们将从基础概念出发,逐步深入到实战技巧,为读者提供一个清晰的后端开发路线图。
|
6天前
|
JSON 前端开发 API
后端开发中的API设计与文档编写指南####
本文探讨了后端开发中API设计的重要性,并详细阐述了如何编写高效、可维护的API接口。通过实际案例分析,文章强调了清晰的API设计对于前后端分离项目的关键作用,以及良好的文档习惯如何促进团队协作和提升开发效率。 ####
|
4天前
|
JSON API 数据格式
如何使用Python开发1688商品详情API接口?
本文介绍了如何使用Python开发1688商品详情API接口,获取商品的标题、价格、销量和评价等详细信息。主要内容包括注册1688开放平台账号、安装必要Python模块、了解API接口、生成签名、编写Python代码、解析返回数据以及错误处理和日志记录。通过这些步骤,开发者可以轻松地集成1688商品数据到自己的应用中。
19 1
|
7天前
|
监控 搜索推荐 安全
探究亚马逊详情API接口:开发与应用
在数字化时代,亚马逊作为全球领先的电商平台,为商家和消费者提供了丰富的商品信息和便捷的购物体验。本文深入探讨了亚马逊详情API接口的获取与运用,帮助开发者和商家实时监控商品数据、分析市场趋势、优化价格策略、分析竞争对手、构建推荐系统及自动化营销工具,从而在竞争中占据优势。文章还提供了Python调用示例和注意事项,确保API使用的安全与高效。
28 3
|
11天前
|
缓存 监控 Java
如何运用JAVA开发API接口?
本文详细介绍了如何使用Java开发API接口,涵盖创建、实现、测试和部署接口的关键步骤。同时,讨论了接口的安全性设计和设计原则,帮助开发者构建高效、安全、易于维护的API接口。
35 4
|
10天前
|
XML JSON API
【PHP开发专栏】PHP RESTful API设计与开发
随着互联网技术的发展,前后端分离成为Web开发的主流模式。本文介绍RESTful API的基本概念、设计原则及在PHP中的实现方法。RESTful API是一种轻量级、无状态的接口设计风格,通过HTTP方法(GET、POST、PUT、DELETE)操作资源,使用JSON或XML格式传输数据。在PHP中,通过定义路由、创建控制器、处理HTTP请求和响应等步骤实现RESTful API,并强调了安全性的重要性。
17 2
|
17天前
|
前端开发 关系型数据库 API
深入浅出后端开发——从零到一构建RESTful API
本文旨在为初学者提供一个关于后端开发的全面指南,特别是如何从零开始构建一个RESTful API。我们将探讨后端开发的基本概念、所需技术栈、以及通过实际案例展示如何设计和实现一个简单的RESTful API。无论你是完全的新手还是有一定编程基础的开发者,这篇文章都将为你提供实用的知识和技巧,帮助你在后端开发的道路上迈出坚实的一步。

热门文章

最新文章