Entity Framework 6 Recipes 2nd Edition(9-4)譯->Web API 的客戶端實現修改跟蹤

来源:http://www.cnblogs.com/kid1412/archive/2016/01/18/5140776.html
-Advertisement-
Play Games

9-4. Web API 的客戶端實現修改跟蹤問題我們想通過客戶端更新實體類,調用基於REST的Web API 服務實現把一個對象圖的插入、刪除和修改等資料庫操作。此外, 我們想通過EF6的Code First方式實現對數據的訪問。本例,我們模擬一個N層場景,用單獨的控制台應用程式作為客戶端,調用W...


9-4. Web API 的客戶端實現修改跟蹤

問題

我們想通過客戶端更新實體類,調用基於REST的Web API 服務實現把一個對象圖的插入、刪除和修改等資料庫操作。此外, 我們想通過EF6的Code First方式實現對數據的訪問。

本例,我們模擬一個N層場景,用單獨的控制台應用程式作為客戶端,調用Web API服務(web api項目)。

註:每個層用一個單獨的解決方案,這樣有助於調試和模擬N層應用。

解決方案

假設我們一個如Figure 9-4.所示模型

Figure 9-4. A 客戶和電話模型

 

我們的模型展示了客戶與對應的電話信息.我們把模型和資料庫代碼封裝至Web Api服務之後,讓客戶端利用HTTP來插入,更新,刪除對象。

以下步驟建立服務項目::

1.新建ASP.NET MVC 4 Web 應用程式,在嚮導中選擇Web API模板,把項目命名為: Recipe4.Service.

2.向項目添加一個新的WEB API控制器,名為: CustomerController.

3. 添加如Listing 9-19所示的BaseEntity類,作為實體類的基類和枚舉類型的TrackingState.基類包含TrackingState屬性,客戶端通過它操縱實體對象, 它接受一個TrackingState枚舉值

註意: TrackingState不會持久化到資料庫

創建我們自己的內部跟蹤狀態枚舉是為了讓客戶端保存實體狀態,這是EF的跟蹤狀態所需要的。DbContext 文件OnModelCreating方法里不映射TrackingState屬性到資料庫

Listing 9-19. Entity Base Class and TrackingState Enum Type

    public class BaseEntity

    {

        protected BaseEntity()

        {

            TrackingState = TrackingState.Nochange;

        }

        public TrackingState TrackingState { get; set; }

      

    }

    public enum TrackingState

    {

        Nochange,

        Add,

        Update,

        Remove,

}

4. 添加Listing 9-20所示的Customer 和Phone實體類

Listing 9-20. Customer and Phone Entity Classes

    public class Customer:BaseEntity

{

        public Customer()

        {

            Phones = new HashSet<Phone>();

    }

        public int CustomerId { get; set; }

        public string Name { get; set; }

        public string Company { get; set; }

        public virtual ICollection<Phone> Phones { get; set; }

}

public class Phone :BaseEntity

    {

        public int PhoneId { get; set; }

        public string Number { get; set; }

        public string PhoneType { get; set; }

        public int CustomerId { get; set; }

        public virtual Customer Customer { get; set; }

 

}

5. 添加EF6的引用。最好是藉助 NuGet 包管理器來添加。在”引用”上右擊,選擇”管理 NuGet 程式包.從“聯機”標簽頁,定位並安裝EF6包。這樣將會下載,安裝並配置好EF6庫到你的項目中.

6. 添加類,名為:Recipe4Context, 鍵入 Listing 9-21 里的代碼,

確保它繼承自 DbContext 類. 註意EF6的“不映射基類型”的配置,我們定義一個”ignore“規範,指定不映射的屬性。

■■註意

=======================================================================

R owan Martin,微軟的EF團隊主管發佈了一篇名為Configuring Unmapped Base Types的博客: http://romiller.com/2013/01/29/ef6-code-first-configuringunmapped-base-types/. 也請認真閱讀Rowan的其它在EF上傑出的博客文章。

=======================================================================

Listing 9-21. Context Class

    public class Recipe4Context:DbContext

    {

        public Recipe4Context() : base("Recipe4ConnectionString") { }

        public DbSet<Customer> Customers { get; set; }

        public DbSet<Phone> Phones { get; set; }

 

        protected override void OnModelCreating(DbModelBuilder modelBuilder)

        {

            //不要把TrackingState 屬性持久化,它只是用來跟蹤與Web api服務脫離的實體的狀態

            //該屬性定義在基類里。

            modelBuilder.Types<BaseEntity>().Configure(x => x.Ignore(y => y.TrackingState));

            modelBuilder.Entity<Customer>().ToTable("Chapter9.Customer");

            modelBuilder.Entity<Phone>().ToTable("Chapter9.Phone");

        }

}

7. 把Listing 9-22所示的 “ Recipe4ConnectionString “連接字元串添加到Web.Config 文件的ConnectionStrings 節里.

Listing 9-22. Connection string for the Recipe1 Web API Service

<connectionStrings>

    <add name="Recipe4ConnectionString"

                  connectionString="Data Source=.;

                  Initial Catalog=EFRecipes;

                  Integrated Security=True;

                  MultipleActiveResultSets=True"

                  providerName="System.Data.SqlClient" />

</connectionStrings>

8. 把Listing 9-23里的代碼添加到Global.asax 文件的Application_Start 方法里. 該代碼禁止EF進行模型相容性檢查,和JSON忽略序列時對象迴圈引用的問題。

Listing 9-23. Disable the Entity Framework Model Compatibility Check

protected void Application_Start()

       {

            //禁止EF進行模型相容性檢查

            Database.SetInitializer<Recipe4Context>(null);

//使JSON序列化器忽略迴圈引用的問題    GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling

                     = Newtonsoft.Json.ReferenceLoopHandling.Ignore;

...

}

9. 添加一個類,名為: EntityStateFactory,插入Listing 9-24里的代碼。該工廠類會把客戶端TrackingState狀態枚舉值轉譯成EF里的實體狀態.

Listing 9-24. Customer Web API Controller

public class EntityStateFactory

    {

        public static EntityState Set(TrackingState trackingState)

        {

            switch (trackingState)

            {

        

                case TrackingState.Add:

                    return EntityState.Added;

                case TrackingState.Update:

                    return EntityState.Modified;

                case TrackingState.Remove:

                    return EntityState.Deleted;

                default:

                    return EntityState.Unchanged;

            }

        }

}

最後, 用Listing 9-25里的代碼替換CustomerController里的。

Listing 9-25. Customer Web API Controller

public class CustomerController : ApiController

    {

        // GET api/customer

        public IEnumerable<Customer> Get()

        {

            using (var context = new Recipe4Context())

            {

                return context.Customers.Include(x => x.Phones).ToList();

            }

        }

 

        // GET api/customer/5

        public Customer Get(int id)

        {

            using (var context = new Recipe4Context())

            {

                return context.Customers.Include(x => x.Phones).FirstOrDefault(x => x.CustomerId == id);

            }

        }

 

        [ActionName("Update")]

        public HttpResponseMessage UpdateCustomer(Customer customer)

        {

            using (var context = new Recipe4Context())

            {

                context.Customers.Add(customer);

                //把對象圖添加到Context(狀態為Added),然後它在客戶端被設置的基數狀態枚舉值翻譯成相應的實體狀態

                //(包括父與子實體,也就是customer和phone)

                foreach (var entry in context.ChangeTracker.Entries<BaseEntity>())

                {

                    entry.State = EntityStateFactory.Set(entry.Entity.TrackingState);

                    if (entry.State == EntityState.Modified)

                    {

                        //先把實體狀態設為'Unchanged'

                        //再讓實體的原始副本等於從資料庫里獲取的副本

                        //這親,EF就會跟蹤每個屬性的狀態,會標誌出被修改過的屬性

                        entry.State = EntityState.Unchanged;

                        var databaseValues = entry.GetDatabaseValues();

                        entry.OriginalValues.SetValues(databaseValues);

                    }

                }

                context.SaveChanges();

            }

            return Request.CreateResponse(HttpStatusCode.OK, customer);

        }

 

        [HttpDelete]

        [ActionName("Cleanup")]

        public HttpResponseMessage Cleanup()

        {

            using (var context = new Recipe4Context())

            {

                context.Database.ExecuteSqlCommand("delete from chapter9.phone");

                context.Database.ExecuteSqlCommand("delete from chapter9.customer");

                return Request.CreateResponse(HttpStatusCode.OK);

            }

        }

}

10. 接下來新建一個解決方案,新建一個Recipe4.Client控制台應用程式,讓它來調用上面創建的服務。

11. 用Listing 9-26.代碼替換program.cs 里的代碼

Listing 9-26. Our Windows Console Application That Serves as Our Test Client

    class Program

    {

        private HttpClient _client;

        private Customer _bush, _obama;

        private Phone _whiteHousePhone, _bushMobilePhone, _obamaMobilePhone;

        private HttpResponseMessage _response;

 

        static void Main(string[] args)

        {

            Task t = Run();

            t.Wait();

            Console.WriteLine("\npress <enter>to continue...");

            Console.ReadLine();

        }

 

        private static async Task Run()

        {

            var program = new Program();

            program.ServiceSetup();

            //等待Cleanup執行結束

            await program.CleanupAsync();

            program.CreateFirstCustomer();

            //等待AddCustomerAsync執行結束

            await program.AddCustomerAsync();

            program.CreateSecondCustomer();

            //等待AddSecondCustomerAsync執行結束

            await program.AddSecondCustomerAsync();

            //等待RemoveFirstCustomerAsync執行結束

            await program.RemoveFirstCustomerAsync();

            //等待FetchCustomersAsync執行結束

            await program.FetchCustomersAsync();

 

        }

 

        private void ServiceSetup()

        {

            //初始化對WEB API服務調用的對象

            _client = new HttpClient { BaseAddress = new Uri("http://localhost:6658/") };

            //添加頭部信息,設為可JSON的類型

            _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        }

 

        private async Task CleanupAsync()

        {

            //調用服務端的cleanup

            _response = await _client.DeleteAsync("api/customer/cleanup/");

        }

 

        private void CreateFirstCustomer()

        {

            //創建1號客戶和他的兩個電話

            _bush = new Customer

            {

                Name = "George Bush",

                Company = "Ex President",

                //設置狀態為Add,以告知服務端該實體是新增的

                TrackingState = TrackingState.Add,

            };

            _whiteHousePhone = new Phone

            {

                Number = "212 222-2222",

                PhoneType = "White House Red Phone",

                //設置狀態為Add,以告知服務端該實體是新增的

                TrackingState = TrackingState.Add,

            };

            _bushMobilePhone = new Phone

            {

                Number = "212 333-3333",

                PhoneType = "Bush Mobile Phone",

                //設置狀態為Add,以告知服務端該實體是新增的

                TrackingState = TrackingState.Add,

            };

            _bush.Phones.Add(_whiteHousePhone);

            _bush.Phones.Add(_bushMobilePhone);

        }

 

        private async Task AddCustomerAsync()

        {

            //構造對服務端UpdateCustomer的調用

            _response = await _client.PostAsync("api/customer/updatecustomer/", _bush,

                new JsonMediaTypeFormatter());

            if (_response.IsSuccessStatusCode)

            {

                //獲取在服務端新創建的包含實際ID值的實體

                _bush = await _response.Content.ReadAsAsync<Customer>();

                _whiteHousePhone = _bush.Phones.FirstOrDefault(x => x.CustomerId == _bush.CustomerId);

                _bushMobilePhone = _bush.Phones.FirstOrDefault(x => x.CustomerId == _bush.CustomerId);

                Console.WriteLine("Successfully created Customer {0} and {1} Phone Number(s)",

                    _bush.Name, _bush.Phones.Count);

                foreach (var phoneType in _bush.Phones)

                {

                    Console.WriteLine("Added Phone Type:{0}", phoneType.PhoneType);

                }

            }

            else

            {

                Console.WriteLine("{0} ({1})", (int)_response.StatusCode, _response.ReasonPhrase);

            }

        }

        private void CreateSecondCustomer()

        {

            //創建第二號客戶和他的電話

            _obama = new Customer

            {

                Name = "Barack Obama",

                Company = "President",

                //設置狀態為Add,以告知服務端該實體是新增的

                TrackingState = TrackingState.Add,

            };

            _obamaMobilePhone = new Phone

            {

                Number = "212 444-4444",

                PhoneType = "Obama Mobile Phone",

                //設置狀態為Add,以告知服務端該實體是新增的

                TrackingState = TrackingState.Add,

            };

            //設置狀態為update,以告知服務端實體是修改的

            _whiteHousePhone.TrackingState = TrackingState.Update;

            _obama.Phones.Add(_obamaMobilePhone);

            _obama.Phones.Add(_whiteHousePhone);

        }

        private async Task AddSecondCustomerAsync()

        {

            //構造調用服務端UpdateCustomer的請求

            _response = await _client.PostAsync("api/customer/updatecustomer/",

                _obama, new JsonMediaTypeFormatter());

            if (_response.IsSuccessStatusCode)

            {

                //獲取服務端新建的含有正確ID的實體

                _obama = await _response.Content.ReadAsAsync<Customer>();

                _whiteHousePhone = _bush.Phones.FirstOrDefault(x => x.CustomerId == _obama.CustomerId);

                _bushMobilePhone = _bush.Phones.FirstOrDefault(x => x.CustomerId == _obama.CustomerId);

                Console.WriteLine("Successfully created Customer {0} and {1} Phone Numbers(s)",

                            _obama.Name, _obama.Phones.Count);

                foreach (var phoneType in _obama.Phones)

                {

                    Console.WriteLine("Added Phone Type: {0}", phoneType.PhoneType);

                }

            }

            else

            {

                Console.WriteLine("{0} ({1})", (int)_response.StatusCode, _response.ReasonPhrase);

            }

        }

 

        private async Task RemoveFirstCustomerAsync()

        {

            //從資料庫中刪除George Bush(客戶)

            //先獲取George Bush實體,用包含一個參數的請求調用服務端的get方法

            var query = "api/customer/" + _bush.CustomerId;

            _response = _client.GetAsync(query).Result;

            if (_response.IsSuccessStatusCode)

            {

                _bush = await _response.Content.ReadAsAsync<Customer>();

                //標誌實體為Remove,告知服務端應該刪除它

                _bush.TrackingState = TrackingState.Remove;

                //由於在刪除父實體前必須刪除子實體,所以必須刪除它的電話

                foreach (var phoneType in _bush.Phones)

                {

                    //標誌實體為Remove,告知服務端應該刪除它

                    phoneType.TrackingState = TrackingState.Remove;

                }

                _response = await _client.PostAsync("api/customer/updatecustomer/",

                    _bush, new JsonMediaTypeFormatter());

                if (_response.IsSuccessStatusCode)

                {

                    Console.WriteLine("Removed {0} from database", _bush.Name);

                    foreach (var phoneType in _bush.Phones)

                    {

                        Console.WriteLine("Remove {0} from data store", phoneType.PhoneType);

                    }

                }

                else

                {

                    Console.WriteLine("{0} ({1})", (int)_response.StatusCode, _response.ReasonPhrase);

                }

            }

            else

            {

                Console.WriteLine("{0} ({1})", (int)_response.StatusCode, _response.ReasonPhrase);

            }

        }

 

        private async Task FetchCustomersAsync()

        {

            //獲取所有現存的客戶及對應的電話

            _response = await _client.GetAsync("api/customer");

            if (_response.IsSuccessStatusCode)

            {

                var customers = await _response.Content.ReadAsAsync<IEnumerable<Customer>>();

                foreach (var customer in customers)

                {

                    Console.WriteLine("Customer {0} has {1} Phone Numbers(s)",

                            customer.Name, customer.Phones.Count());

                    foreach (var phoneType in customer.Phones)

                    {

                        Console.WriteLine("Phone Type: {0}", phoneType.PhoneType);

                    }

                }

            }

            else

            {

                Console.WriteLine("{0} ({1})", (int)_response.StatusCode, _response.ReasonPhrase);

            }

        }

}//end class program

12. 最後,添加與服務端同樣的Customer, Phone, BaseEntity, 和TrackingState(同Listing 9-19 and 9-20).

下麵Listing 9-26是控制台的輸出結果:

===================================================================

Successfully created Customer Geroge Bush and 2 Phone Numbers(s)

Added Phone Type: White House Red Phone

Added Phone Type: Bush Mobile Phone

Successfully created Customer Barrack Obama and 2 Phone Numbers(s)

Added Phone Type: Obama Mobile Phone

Added Phone Type: White House Red Phone

Removed Geroge Bush from database

Remove Bush Mobile Phone from data store

Customer Barrack Obama has 2 Phone Numbers(s)

Phone Type: White House Red Phone

Phone Type: Obama Mobile Phone

===================================================================

它是如何工作的?

首先啟動Web API應用程式. 當看到首頁就服務已經可用,接著打開控制台應用程式,在. program.cs首行代碼前加斷點,運行,我們首先創建與Web API管道連接並配置頭部多媒體信息類型,使它請求可以接收JSON格式.接著用HttpClient的DeleteAsyn請求,調用服務端的Cleanup方法,從而清除資料庫里之前保存的結果。

接下來我們創建一個新的Customer和兩個phone對象,註意我們是如何明確地為每個實體設置它的TrackingState屬性來告訴EF狀態跟蹤為每個實體產生什麼樣的SQL語句。

接著利用httpclient的PostAsync方法調用服務端的UpdateCustomer方法.如果你在WEB API項目的控制器的UpdateCustomer 方法前加斷點,你會看到該方法參數接收到一個Customer 對象,然後它立刻添加到context 對象里,指定實體為added,並會跟蹤它的狀態。

有興的是:我們接著鉤住context對象比較隱蔽的DbChangeTracker屬性

DbChangeTracker用<DbEntityEntry>的方式為實體暴露一個常用的Ienumerable類型. 我們簡單地分配基類的EntityType 屬性給它,能這麼做是因為我們所有的實體都是繼承自BaseEntity

. 每次迴圈我們都調用一次EntityStateFactory的Set方法,來確定實體的狀態,如果EntityType屬性值為Add,則實體狀態為Added, 如果EntityType屬性值為Update, 則實體狀態為Modified,如果實體狀態為Modified,我們需要做些額外的處理,我們先把實體狀態從Modified改為Unchanged

然後調用GetDatabaseValues 方法,獲得資料庫的值版本,然後賦給實體的OriginalValues ,這個EF跟蹤引擎會檢測到實體的哪個屬性的原始值與當前值不同,並把這些屬性狀態設為modified

. 隨後的 SaveChanges 操作將會僅更新這些修改過的屬性,並生成相應的SQL語句。

再看客戶端程式,演示了通過設置TrackingState屬性來添加,修改,和刪除實體對象

服務端的UpdateCustomer方法簡單地把TrackingState翻譯成了實體的狀態,cotext對象會根據這些狀態產生相應的SQL語句。

在本小節,我們看到了,可以將EF的數據操作封裝到Web API 服務里。客戶端可以通過HttpClient對象調用這些服務. 讓所有實體繼承於一個擁有TrackingState屬性的基類,客戶端通過設置這個屬性,讓服務端的EF跟蹤引擎生成對應的SQL語句。

在實際應用中,我們可能更喜歡創建一個新的層(Visual Studio 類庫),把EF的數據操作從WEB API服務里分離出來,作為一個單獨的層。

本節更重要的是,我們不難做到讓客戶端使用普通的類型來跟蹤實體的變化. 而這個功能可重用於我們其它的項目。

附:創建示例用到的資料庫的腳本文件


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在用Linq查詢中,常常需要用到分頁功能,因為每次都需要些分頁這些功能,於是把分頁功能提取出來,不喜大家勿噴,只是貼出來,自覺地很實用。一下貼出核心代碼: 1 /// 2 /// LinqHelper 主要用於數據集,排序 分頁等功能 3 /// 4 public c...
  • 需要下載NeatUpload插件上傳頁面: 上 傳 查看下載 後臺源碼:protected void BtnUP_Click(object send...
  • 相同點:都是輸出參數不同點:ref:1、必須初始化,即:必須賦初始值;2.有進有出;3、用在需要被調用的方法修改調用者的引用的時候。4、是傳遞參數的地址out:1.不需要初始化,即:不需要賦初始值;2、只出不進;3、用在需要retrun多個返回值的地方;4、返回值;通過代碼更直觀的看出它們的區別:c...
  • --1.使用事務定義與提交命令在資料庫studentcourse中創建一個--“綜合表”(學號,姓名,性別,民族),併為它插入3行數據,觀察--提交之前和之後的瀏覽與回滾情況。create table zonghe(sno varchar(10) primary key,sname varchar(...
  • 1. 導出最後一次提交修改過的文件 我一直在使用這個命令定期進行發送給其他人進行審查/整合。這條命令將把近期提交的修改過的文件導出到一個zip文件。1git archive -o ../updated.zip HEAD $(git diff --name-only HEAD^)2. 導出兩次提交之....
  • 在vs中,如果要使用柱形圖,我們大多數使用第三方提供的插件,所以必須要引用樣式,這裡我使用的是Highcharts-4.1.9插件,百度一下就可以下載到。關鍵的js代碼: .net後臺獲取資料庫數據填充:public string returnValue = "";p...
  • ModelState.Remove("Name") 去掉不需要驗證的屬性。
  • 因為看了《Entity Framework 6 Recipes 2nd Edition》這本書前面8章的翻譯,感謝china_fucan.從第九章開始,我是邊看邊譯的,沒有通讀,加之英語造詣,所以紕漏百出,也請勿見笑,就當是鼓勵!19-1 用Web Api更新單獨分離的實體29-2 用WCF更新單獨...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...