溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何淺談WebService的版本兼容性設(shè)計(jì)

發(fā)布時(shí)間:2021-11-17 13:38:01 來(lái)源:億速云 閱讀:143 作者:柒染 欄目:web開(kāi)發(fā)

如何淺談WebService的版本兼容性設(shè)計(jì),很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。

在現(xiàn)在大型的項(xiàng)目或者軟件開(kāi)發(fā)中,一般都會(huì)有很多種終端, PC端比如Winform、WebForm,移動(dòng)端,比如各種Native客戶端(iOS, Android, WP),Html5等,我們要滿足以上所有這些客戶端的需求,實(shí)現(xiàn)前后端的分離,一種最常見(jiàn)的做法是,編寫(xiě)WebService API來(lái)為以上客戶端提供數(shù)據(jù)。近年來(lái)越來(lái)越多的企業(yè)或者網(wǎng)站支持Restfull方式的WebServiceAPI,比如當(dāng)當(dāng)網(wǎng)開(kāi)源Dubbox,擴(kuò)展Dubbo服務(wù)框架支持REST風(fēng)格遠(yuǎn)程調(diào)用,這個(gè)是Java版本的,在.NET中ServiceStack天生支持Restfull風(fēng)格的WebService。以ServiceStack為基礎(chǔ)探討,淺談WebService的兼容性設(shè)計(jì)。

1.軟件的兼容性

在軟件持續(xù)更新升級(jí)的過(guò)程中,API 也是需要不斷更新,這時(shí)就需要考慮客戶端升級(jí)以及兼容性的問(wèn)題。當(dāng)前有很多用戶可能由于多種原因,尤其是Native用戶,不可能及時(shí)升級(jí)到***版,所以需要提供對(duì)老版本的API的向后兼容。在API設(shè)計(jì)之初,我們需要考慮一些問(wèn)題以及解決方法。

后向兼容性(Backward_compatibility),或者向下兼容,是指對(duì)于給定的輸入,較老版本的產(chǎn)品或者技術(shù),也能夠輸出相同的結(jié)果。如果一個(gè)產(chǎn)品或者API在設(shè)計(jì)之初就能夠?yàn)樾碌臉?biāo)準(zhǔn)考慮,能夠滿足接收,讀取,查看舊的標(biāo)準(zhǔn)或者格式,那么這個(gè)產(chǎn)品就稱之為后向兼容,比如一些數(shù)據(jù)格式或者通訊協(xié)議,在新版本推出時(shí)都會(huì)充分考慮后向兼容問(wèn)題。如果對(duì)一個(gè)產(chǎn)品的改進(jìn)破壞了后向兼容性,則稱之為破壞性的改動(dòng)(breaking change),相信大家都遇到過(guò)這種情況。

  • App長(zhǎng)久沒(méi)更新,落后很多個(gè)版本之后,再次打開(kāi)改App會(huì)提示升級(jí)到***版,或者直接幫你強(qiáng)制升級(jí)。

  • 使用新版的TortoiseSVN打開(kāi)老版本TortoiseSVN創(chuàng)建的工程的時(shí)候,會(huì)提示需要升級(jí)項(xiàng)目工程才能打開(kāi)。

這種情況一般發(fā)生在版本的改動(dòng)比較大,或者對(duì)較老版本的支持成本比較大,在這種情況下,一般還需要為客戶提供從老版本遷移到新版本的工具或者解決方案。

兼容性有很多種類型比如 API 的兼容, 二進(jìn)制dll的兼容性,以及數(shù)據(jù)文檔的兼容。

關(guān)于API的兼容性其實(shí)涉及到API的設(shè)計(jì)。相關(guān)文檔和書(shū)籍有很多,關(guān)于API設(shè)計(jì)的書(shū)可以參考Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries (2nd Edition) 和 How to Design a Good API and Why it Matters

本文主要探討WebService開(kāi)發(fā)的API的向后兼容性。

2. WebService 的后向兼容性

在關(guān)于開(kāi)發(fā)WebService框架上,這里不免又要談一下WCF和ServiceStack的設(shè)計(jì)理念和區(qū)別。

在ServiceStack中,鼓勵(lì)使用Message-based 式的設(shè)計(jì),因?yàn)檫h(yuǎn)程服務(wù)調(diào)用是很耗時(shí),我們應(yīng)該盡量一次多傳輸需要處理的數(shù)據(jù),而避免來(lái)回多次調(diào)用。在WCF中,通過(guò)一些工具,使得開(kāi)發(fā)者能夠像調(diào)用本地方法一樣調(diào)用遠(yuǎn)程方法,這樣會(huì)使人產(chǎn)生誤解,實(shí)際上調(diào)用遠(yuǎn)程方法會(huì)比調(diào)用遠(yuǎn)程方法慢上成千上萬(wàn)倍。ServiceStack在設(shè)計(jì)之初就受Martine Flowler 的 Data Transfer Object 模式的啟發(fā):

“ When you're working with a remote interface, such as Remote Facade (388), each call to it is expensive. As a result you need to reduce the number of calls, and that means that you need to transfer more data with each call. One way to do this is to use lots of parameters. However, this is often awkward to program - indeed, it's often impossible with languages such as Java that return only a single value.

The solution is to create a Data Transfer Object that can hold all the data for the call. It needs to be serializable to go across the connection. Usually an assembler is used on the server side to transfer data between the DTO and any domain objects. ”

在API的設(shè)計(jì)上WCF鼓勵(lì)將WebService作為普通的C#方法調(diào)用,這是一種基于普通的基于PRC 方式的調(diào)用。比如:

public interface IWcfCustomerService {     Customer GetCustomerById(int id);     List<Customer> GetCustomerByIds(int[] id);     Customer GetCustomerByUserName(string userName);     List<Customer> GetCustomerByUserNames(string[] userNames);     Customer GetCustomerByEmail(string email);     List<Customer> GetCustomerByEmails(string[] emails); }

以上WebService方法就是通過(guò)id,username或者email獲取用戶或者用戶列表。如果使用ServiceStack的基于Message-base風(fēng)格的API設(shè)計(jì),接口就是:

public class Customers : IReturn<List<Customer>> {     public int[] Ids { get; set; }     public string[] UserNames { get; set; }     public string[] Emails { get; set; } }

在ServiceStack中,所有的請(qǐng)求信息都包裝在這個(gè)Customers的DTO中,他并不依賴于服務(wù)端方法的簽名。最簡(jiǎn)單的好處在于使用message-base的設(shè)計(jì)在于wcf中的任意RPC組合都可以使用一個(gè)ServiceStack中的遠(yuǎn)程消息組合,并且只需要服務(wù)端的一次實(shí)現(xiàn)。

閑話說(shuō)了這么多,現(xiàn)在來(lái)看看如何設(shè)計(jì)WebService的后向兼容性。談到WebService,大家都會(huì)想到WCF,關(guān)于WCF的后向兼容,在Codeproject上,有人寫(xiě)了三篇文章WCF Backwards Compatibility and Versioning Strategies(part1,part2,part3),由于ServiceStack僅支持Poco方式的請(qǐng)求參數(shù),并且寫(xiě)在Poco中的字段都是必須的,沒(méi)有WCF 中的對(duì)字段的 [DataMember(IsRequired = true)] 和 [DataMember(IsRequired = false)] 來(lái)標(biāo)識(shí)字段是否可選,所以WCF支持的RPC方式的參數(shù)(Part1文章中的后向兼容算法)ServiceStack中無(wú)法做測(cè)試,這里對(duì)比做Part2文章中的測(cè)試。并且測(cè)試的時(shí)候,測(cè)試添加和移除字段對(duì)Service調(diào)用的影響。

建立測(cè)試之前,我們先建立一個(gè)基本的ServiceStack程序。 這個(gè)程序和前文中介紹一樣,是一個(gè)簡(jiǎn)單的 ServiceStack序。

3. 基礎(chǔ)

使用ServiceStack創(chuàng)建服務(wù),基本的工程結(jié)構(gòu)有三個(gè)。

如何淺談WebService的版本兼容性設(shè)計(jì)

ServiceModel這一層主要是定義 WebService中的請(qǐng)求參數(shù)和返回參數(shù)DTO, Employ中的代碼如下:

namespace WebApplication1.ServiceModel {     [Route("/Employ/{EmpId}/{EmpName}")]     public class Employ : IReturn<EmployResponse>     {         public string EmpId { get; set; }         public string EmpName { get; set; }      }      public class EmployResponse     {         public string EmpId { get; set; }         public string EmpName { get; set; }     } }

代碼定義了請(qǐng)求參數(shù)DTO Employ對(duì)象,約定了其返回類型為 EmployResponse,這里繼承IReturn< EmployResponse >是為了方便測(cè)試。 這里面指定了這個(gè)WebService的請(qǐng)求對(duì)象是Employ,返回對(duì)象是EmployResponse,并且通過(guò)&rsquo; /Employ/{EmpId}/{EmpName}&rsquo;這樣的方式來(lái)調(diào)用服務(wù)為Employ對(duì)象賦值。

ServiceInterface這一層是服務(wù)實(shí)現(xiàn)層。里面的EmployServices直接繼承自ServiceStack中的Service對(duì)象。

namespace WebApplication1.ServiceInterface {     public class EmployServices : Service     {         public EmployResponse Any(Employ request)         {             return new EmployResponse { EmpId = request.EmpId, EmpName = request.EmpName};         }     } }

這里Any表示這個(gè)Restfull請(qǐng)求支持Post和Get兩種方式,請(qǐng)求參數(shù)類型Hello和返回值類型EmployResponse在Model中已經(jīng)定義。我們不關(guān)心這個(gè)方法的名稱,因?yàn)榭梢酝ㄟ^(guò)Rest路由來(lái)進(jìn)行訪問(wèn)。

WebApplication和ConsoleApplicaiton是ServiceInterface的服務(wù)宿主層,我們可以使用ASP.NET 將服務(wù)部署到IIS上,也可以通過(guò)控制臺(tái)程序進(jìn)行部署以方便測(cè)試。

Web宿主很簡(jiǎn)單,我們定義一個(gè)類繼承自AppHostbase,并提供包含有服務(wù)的程序集即可:

namespace WebApplication1 {     public class AppHost : AppHostBase     {         /// <summary>         /// Default constructor.         /// Base constructor requires a name and assembly to locate web service classes.          /// </summary>         public AppHost()             : base("WebApplication1", typeof(EmployServices).Assembly)         {          }          /// <summary>         /// Application specific configuration         /// This method should initialize any IoC resources utilized by your web service classes.         /// </summary>         /// <param name="container"></param>         public override void Configure(Container container)         {             //Config examples             //this.AddPlugin(new PostmanFeature());             //this.AddPlugin(new CorsFeature());         }     } }

然后,在網(wǎng)站啟動(dòng)的時(shí)候,在Application_Start方法中初始化即可:

namespace WebApplication1 {     public class Global : System.Web.HttpApplication     {         protected void Application_Start(object sender, EventArgs e)         {             new AppHost().Init();         }     } }

現(xiàn)在我們就可以通過(guò)Web的方式來(lái)查看我們創(chuàng)建的service服務(wù):

如何淺談WebService的版本兼容性設(shè)計(jì)

可以通過(guò)Post Http的方式采用Json格式調(diào)用WebService服務(wù),比如我們可以構(gòu)造Json格式,將內(nèi)容Post到 地址,http://localhost:28553/json/reply/ Employ:

{"EmpId":"p1","EmpName":"zhangsan"}

返回值為:

{"EmpId":"p1","EmpName":"zhangsan"}

或者直接在地址欄里輸入:http://localhost:28553/Employ/p1/zhangshan

如何淺談WebService的版本兼容性設(shè)計(jì)

不過(guò)在開(kāi)發(fā)的時(shí)候,我們通常采用***種方式,將參數(shù)序列化為json字符串,然后post到我們部署的地址上。

以上是服務(wù)端代碼,部署好了之后,客戶端需要進(jìn)行調(diào)用,調(diào)用的時(shí)候,我們需要引用ServiceModel這里面的請(qǐng)求和返回值實(shí)體類型。在部署了WebService之后,我們也可以通過(guò)引用WebService的方式來(lái)進(jìn)行引用字段。

新建一個(gè)控制臺(tái)應(yīng)用程序,將上面的ServiceModel編譯為dll之后,拷貝到新建的控制臺(tái)程序下面,然后引用這個(gè)dll,客戶端調(diào)用代碼如下,我們采用了Json的方式傳送數(shù)據(jù),當(dāng)然您可以選擇其他的數(shù)據(jù)格式進(jìn)行傳輸。代碼如下:

class Program {     static void Main(string[] args)     {         Console.Title = "ServiceStack Console Client";          using (var client = new JsonServiceClient("http://localhost:28553/"))         {             EmployResponse employResponse = client.Send<EmployResponse>(new Employ { EmpId="1", EmpName="zhangshan"});             if (employResponse != null)             {                 Console.WriteLine(string.Format("EmoplyId:{0},EmployName:{1}",employResponse.EmpId,employResponse.EmpName));             }         }          Console.ReadKey();     } }

把服務(wù)端代碼運(yùn)行起來(lái)之后,然后運(yùn)行上面的控制臺(tái)程序,輸出如下:

EmoplyId:p1,EmployName:zhangshan

4. 測(cè)試

4.1 Case 1 添加新字段

現(xiàn)在假設(shè)我們v1版本的API中Employ實(shí)體只有兩個(gè)字段,后來(lái)我們發(fā)現(xiàn),在v2版本中,還需要添加一個(gè)Address字段,以表示該雇員的地址,于是我們修改了Model,添加了Address字段,在Request和Response中均修改了該字段,現(xiàn)在服務(wù)端代碼如下:

namespace WebApplication1.ServiceModel {     [Route("/Employ/{EmpId}/{EmpName}")]     public class Employ : IReturn<EmployResponse>     {         public string EmpId { get; set; }         public string EmpName { get; set; }         public string Address { get; set; }      }      public class EmployResponse     {         public string EmpId { get; set; }         public string EmpName { get; set; }         public string Address { get; set; }     } } namespace WebApplication1.ServiceInterface {     public class EmployServices : Service     {         public EmployResponse Any(Employ request)         {             return new EmployResponse { EmpId = request.EmpId, EmpName = request.EmpName, Address = request.Address };         }     } }

然后編譯運(yùn)行。需要注意的是,客戶端引用的ServiceModel這個(gè)dll,依然是之前的老版本的只有兩個(gè)字段的dll,現(xiàn)在再次運(yùn)行客戶端,輸出如下:

EmoplyId:0,EmployName:zhangshan

結(jié)果和之前的一抹一樣,這表示,對(duì)新的API添加新的字段和在返回值中添加新的字段,不會(huì)對(duì)就有的WebService產(chǎn)生影響。

4.2 Case 2 :修改數(shù)據(jù)字段的類型

再后來(lái),在V3版本中,我們發(fā)現(xiàn)EmpID應(yīng)該是一個(gè)int型,于是我們將服務(wù)端的Employ實(shí)體的EmployID從string類型改為了int型,然后運(yùn)行客戶端,因?yàn)樵诳蛻舳耍覀儌鹘oID的是string類型的”p1”該類型不能直接轉(zhuǎn)換為int型,真實(shí)的輸出的結(jié)果是:

EmoplyId:0,EmployName:zhangshan

沒(méi)有報(bào)錯(cuò),但是不是我們期望的結(jié)果??蛻舳藢mpolyID字段傳”p1”過(guò)去的時(shí)候,服務(wù)端該字段類型已經(jīng)變更為了int,”p1”沒(méi)有轉(zhuǎn)換為int型,所以會(huì)使用int的默認(rèn)初始值0代替。

4.3 Case 3:移除必要字段

現(xiàn)在我們編譯一下ServiceModel,然后拷貝到ServiceClint更新一下客戶端的dll引用,這樣客戶端就能夠獲取到Address這個(gè)字段了。如果是WebService的話,直接更新一下引用就可以?,F(xiàn)在我們修改客戶端,請(qǐng)求的時(shí)候?yàn)锳ddress賦值。

static void Main(string[] args)     {         Console.Title = "ServiceStack Console Client";          using (var client = new JsonServiceClient("http://localhost:28553/"))         {             EmployResponse employResponse = client.Send<EmployResponse>(new Employ { EmpId="p1", EmpName="zhangshan",Address="sh"});             if (employResponse != null)             {                 Console.WriteLine(string.Format("EmoplyId:{0},EmployName:{1},Work at:{2}",employResponse.EmpId,employResponse.EmpName,employResponse.Address));             }         }          Console.ReadKey();     }

可以看到這是客戶端已經(jīng)更新EmpId已經(jīng)是int型了,如果在傳p1的話,會(huì)報(bào)錯(cuò)?,F(xiàn)在編譯運(yùn)行,輸出結(jié)果應(yīng)該是:

EmoplyId:1,EmployName:zhangshan,Work at:sh

現(xiàn)在,在V4版本中,我們發(fā)現(xiàn)v2版本中添加的工作地址Address這個(gè)字段不應(yīng)該放在Employ中,所以在服務(wù)端將該字段移除,并進(jìn)行了重新部署。客戶端端再次運(yùn)行,結(jié)果如下:

如何淺談WebService的版本兼容性設(shè)計(jì)

EmoplyId:1,EmployName:zhangshan,Work at:

可以看到,服務(wù)端去除了Address字段后,服務(wù)端返回的原始數(shù)據(jù)中缺失Address元素,客戶端在反序列化的時(shí)候找不帶該字段就賦值為了null。

5. 總結(jié)

如果使用ServiceStack,在API的進(jìn)化過(guò)程中,新版本的API可能較老版本的API會(huì)有如下修改:

  • 添加,移除,或者重命名字段。

  • 將字段的類型從int轉(zhuǎn)換為了double或者long,這種可以隱式轉(zhuǎn)換的類型。

  • 將集合類型從List改為了HashSet。

  • 將強(qiáng)類型集合改為了松散類型的List集合改為了松散的List里面的元素為Dictionary 。

  • 添加了新的枚舉類型。

  • 添加了可空的字段。

在客戶端序列化實(shí)體后,傳到服務(wù)端的時(shí)候,序列化工具會(huì)自動(dòng)的處理以上類型的變更和不一致,如果字段對(duì)應(yīng)不上,或者類型轉(zhuǎn)換不過(guò)去,則會(huì)使用服務(wù)端的字段的類型默認(rèn)值替代。

對(duì)于API的兼容性策略,有以下幾個(gè)注意點(diǎn):

5.1應(yīng)該對(duì)現(xiàn)有API版本的進(jìn)化來(lái)解決,而不是重新實(shí)現(xiàn)一個(gè)

僅添加了一個(gè)新字段的參數(shù)。因?yàn)?,如果要同時(shí)維護(hù)多個(gè)不同版本的,但是實(shí)現(xiàn)相同功能的API可能會(huì)使得工作量巨大,而且容易出錯(cuò)。在編寫(xiě)***個(gè)版本的API之初,就應(yīng)該遵守這一約定。同時(shí)編寫(xiě)多個(gè)版本的實(shí)現(xiàn)相同或相似功能的API違反了DRY原則。

5.2要充分利用自建的序列化工具的版本功能優(yōu)勢(shì)

一些序列化工具會(huì)在字段對(duì)應(yīng)不上的時(shí)候,給字段附上改字段類型的默認(rèn)值;能夠在相同結(jié)構(gòu)的集合類型之間進(jìn)行自動(dòng)轉(zhuǎn)換;能夠進(jìn)行類型的隱式轉(zhuǎn)換等等。比如,如果一個(gè)id在較老的api中使用的是int類型,那么在新版本中,我們可以直接將其更改為long類型就可以向后兼容。

增強(qiáng)現(xiàn)有服務(wù)對(duì)變化的防御性

在WCF中,使用DataContract可以自由添加或者移除字段而不會(huì)產(chǎn)生breaking change。這主要是在于其實(shí)現(xiàn)了IExtensibleDataObject接口。在返回類型的DTO對(duì)象上,如果實(shí)現(xiàn)該接口,也能實(shí)現(xiàn)該功能。在兼容舊版本的DTOs的時(shí)候,要做好充分測(cè)試,一般的:

  1. 不要修改已存在字段的數(shù)據(jù)類型,如果確實(shí)需要修改,添加另外一個(gè)屬性,并且根據(jù)具有的字段來(lái)判斷版本。

  2. 防御性編程,要判斷在老版本客戶端中那些字段可能不存在,所以不要強(qiáng)制認(rèn)為一定需要存在。

  3. 保證只有一個(gè)唯一的全局命名空間。這個(gè)可以在程序的AssemblyInfo.cs中處理,一般的我們定義一個(gè)公共的AssemblyInfo,然后在各個(gè)DTO項(xiàng)目中,進(jìn)行鏈接引用。

5.3 在DTO中添加版本控制信息

這個(gè)也是最容易想到和實(shí)現(xiàn)的。在大多數(shù)情況下,如果使用防御性編程的思想并且對(duì)API進(jìn)行平滑演進(jìn)的話,通常可以根據(jù)數(shù)據(jù)來(lái)推斷出客戶端的版本。但是在一些特殊情況下,服務(wù)端需要根據(jù)客戶端的特定版本來(lái)處理相應(yīng),因此可以在請(qǐng)求DTO中添加版本信息。舉例如下:

比如在最初發(fā)布Empoly這個(gè)服務(wù)的時(shí)候,沒(méi)有多想直接定義了下面這個(gè)請(qǐng)求的DTO:

class Employ   {     string Name { get; set; } }

然后由于某些原因應(yīng)用的UI發(fā)生了變化,我們不想給客戶返回這個(gè)籠統(tǒng)的Name屬性,需要追蹤客戶端使用的版本,來(lái)考慮返回那個(gè)值,于是DTO修改為了如下,并且添加了新字段DisplayName和Age:

class Employ   {     Employ()     {         Version = 1;     }     int Version;     string Name;     string DisplayName;     int Age; }

然而,經(jīng)過(guò)會(huì)議討論,發(fā)現(xiàn)DisplayName仍然不太好,***能夠把它拆分成兩個(gè)字段,并且存儲(chǔ)Age不好,應(yīng)該存儲(chǔ)DateOfBirth 于是我們的DTO變成了這樣:

class Employ {     Employ()     {         Version = 2;     }     int Version;     string Name;     string DisplayName;     string FirstName;     string LastName;     DateTime? DateOfBirth; }

到目前位置,客戶端有三個(gè)版本,他們給服務(wù)端發(fā)送請(qǐng)求如下:

V1版本:

client.Send<EmployResponse>(new Employ { Name = "zhangshan" });

V2版本:

client.Send<EmployResponse>(new Employ { Name = "zhangshan" DisplayName="Ms Zhang", Age=22 });

V3版本:

client.Send<EmployResponse>(new Employ { FirstName = "zhang" LastName="shan", DateOfBirth=new DateTime(1992,01,01) });

現(xiàn)在,服務(wù)端可以統(tǒng)一處理以上三個(gè)版本客戶端的請(qǐng)求:

public class EmployServices : Service {     public EmployResponse Any(Employ request)     {         //V1:         request.Version == 0;         request.Name = "zhangshan";         request.DisplayName == null;         request.Age = 0;         request.DateOfBirth = null;          //v2:         request.Version == 2;         request.Name == null;         request.DisplayName == "Foo Bar";         request.Age = 18;         request.DateOfBirth = null;          //v3:         request.Version == 3;         request.Name == null;         request.DisplayName == null;         request.FirstName == "Foo";         request.LastName == "Bar";         request.Age = 0;         request.DateOfBirth = new DateTime(1994, 01, 01);         //.....     } }

5.4 要遵循”最小化”原則

***一點(diǎn)應(yīng)該是在實(shí)踐中的經(jīng)驗(yàn)總結(jié)。和我們?cè)賁QLServer中寫(xiě)查詢條件一樣,千萬(wàn)不要圖方便使用select * 來(lái)代替要用到的需要手動(dòng)輸入的字段,除去效率原因,因?yàn)橐坏┳侄尾樵兊淖侄雾樞虬l(fā)生變動(dòng),可能就會(huì)影響到解析。在設(shè)計(jì)DTO的可選值的時(shí)候,也需要考慮這樣的問(wèn)題。這里舉一個(gè)例子:

比如我們要設(shè)計(jì)一個(gè)查找附件商戶的API,該API支持查找附件的酒店,餐飲以及不限。所以我們一般會(huì)在DTO中定義一個(gè)表示查找范圍的字段SearchScope,并定義一些枚舉值。

0-不限 1-酒店 2-餐飲

這里需要注意的是,如果我們?cè)?**個(gè)版本中,一開(kāi)始就使用0表示不限的話,在實(shí)現(xiàn)中,比如在SQL語(yǔ)句中,我們通常會(huì)不會(huì)查詢的條件作出限制,這樣就正常的發(fā)出了***個(gè)版本。

然而,在第二個(gè)版本中,我們添加的對(duì)附近娛樂(lè)場(chǎng)所的支持,并且開(kāi)發(fā)了對(duì)娛樂(lè)場(chǎng)所搜索結(jié)果的特殊頁(yè)面支持。于是,自然而然的考慮到在SearchScope中添加一個(gè)枚舉值表示娛樂(lè),并且在DB也增加了對(duì)娛樂(lè)場(chǎng)所信息的存儲(chǔ)。

3- 娛樂(lè)

然后V2版本的接口順利發(fā)布了。這個(gè)時(shí)候,如果DTO中沒(méi)有版本信息,問(wèn)題就來(lái)了,在V1版本中,當(dāng)用戶搜索的時(shí)候,如果選擇不限,那么搜索結(jié)果中就會(huì)出現(xiàn) “娛樂(lè)” 相關(guān)信息,但是點(diǎn)擊搜索出來(lái)的”娛樂(lè)”的結(jié)果的時(shí)候,其他頁(yè)面在V1版本的時(shí)候,沒(méi)有做相應(yīng)的頁(yè)面處理。所以就會(huì)出現(xiàn)一些問(wèn)題。所以發(fā)布V2版本的API的時(shí)候,需要修改V1版本的處理邏輯。

所以當(dāng)初在設(shè)計(jì)V1版本的API 的時(shí)候,對(duì)于條件或者組合不太多的情況,對(duì)于”不限”這種場(chǎng)景,我們應(yīng)該是傳所有支持的類別,比如傳”1,2”而不是”0”?;蛘咴谠O(shè)計(jì)范圍的時(shí)候,設(shè)計(jì)成可以進(jìn)行或”|”方式的枚舉值,比如設(shè)計(jì)成 0-不限,1-酒店,2-餐飲,4-娛樂(lè)等。這樣新版本發(fā)布后對(duì)叫老版本的API影響比較少。

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI