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

相关文章
|
8天前
|
安全 Java API
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)
28 0
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
|
30天前
|
前端开发 JavaScript API
基于React的简易REST API客户端设计与实现
基于React的简易REST API客户端设计与实现
21 3
|
1月前
|
缓存 安全 测试技术
构建高效的RESTful API:后端开发的实践指南
【2月更文挑战第17天】在数字化转型的浪潮中,RESTful API已成为连接不同软件组件、实现数据交互的核心桥梁。本文将深入探讨如何构建一个高效、可扩展且安全的RESTful API,涉及设计原则、开发流程以及性能优化等关键方面。我们将透过实际案例,展示如何在保证简洁性和灵活性的同时,满足日益增长的业务需求和技术挑战。
|
1月前
|
API
uni-app 146朋友圈列表api开发
uni-app 146朋友圈列表api开发
18 0
|
4天前
|
人工智能 机器人 API
【Python+微信】【企业微信开发入坑指北】3. 如何利用企业微信API给微信群推送消息
【Python+微信】【企业微信开发入坑指北】3. 如何利用企业微信API给微信群推送消息
7 0
|
4天前
|
缓存 人工智能 API
【Python+微信】【企业微信开发入坑指北】2. 如何利用企业微信API主动给用户发应用消息
【Python+微信】【企业微信开发入坑指北】2. 如何利用企业微信API主动给用户发应用消息
8 0
|
15天前
|
小程序 前端开发 API
小程序全栈开发中的RESTful API设计
【4月更文挑战第12天】本文探讨了小程序全栈开发中的RESTful API设计,旨在帮助开发者理解和掌握相关技术。RESTful API基于REST架构风格,利用HTTP协议进行数据交互,遵循URI、客户端-服务器架构、无状态通信、标准HTTP方法和资源表述等原则。在小程序开发中,通过资源建模、设计API接口、定义资源表述及实现接口,实现前后端高效分离,提升开发效率和代码质量。小程序前端利用微信API与后端交互,确保数据流通。掌握这些实践将优化小程序全栈开发。
|
24天前
|
前端开发 Java API
构建RESTful API:Java中的RESTful服务开发
【4月更文挑战第3天】本文介绍了在Java环境中构建RESTful API的重要性及方法。遵循REST原则,利用HTTP方法处理资源,实现CRUD操作。在Java中,常用框架如Spring MVC简化了RESTful服务开发,包括定义资源、设计表示层、实现CRUD、考虑安全性、文档和测试。通过Spring MVC示例展示了创建RESTful服务的步骤,强调了其在现代Web服务开发中的关键角色,有助于提升互操作性和用户体验。
构建RESTful API:Java中的RESTful服务开发
|
29天前
|
机器学习/深度学习 前端开发 API
实现以图搜货功能,淘宝API开发实战分享
实现以图搜货功能,淘宝API开发实战分享
24 0
|
1月前
|
缓存 前端开发 API
构建高效可扩展的RESTful API:后端开发的最佳实践
【2月更文挑战第30天】 在现代Web应用和服务端架构中,RESTful API已成为连接前端与后端、实现服务间通信的重要接口。本文将探讨构建一个高效且可扩展的RESTful API的关键步骤和最佳实践,包括设计原则、性能优化、安全性考虑以及错误处理机制。通过这些实践,开发者可以确保API的健壮性、易用性和未来的可维护性。