1. 程式人生 > >UWP GraphQL資料查詢客戶端的實現

UWP GraphQL資料查詢客戶端的實現

1. 緣起

Facebook 的移動應用從 2012 年就開始使用 GraphQL。GraphQL 規範於 2015 年開源,現已經在多種環境下可用,並被各種體量的團隊所使用。

在這個連結可以看到更多的GraphQL使用者。

 

 

 

2. GraphQL是什麼

英文官網:GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data.

中文官網:GraphQL 既是一種用於 API 的查詢語言也是一個滿足你資料查詢的執行時。

個人理解:GraphQL利用了傳統的SQL查詢,並且以restful api的形式返回資料。

舉個栗子:

傳統的Restful api查詢,你要查詢一個使用者的頭像資訊,不需要別的資訊,那麼就是需要利用Http傳送給服務區user_id,然後伺服器返回給你這個使用者的所有資訊,比如郵箱、暱稱、電話、頭像等資訊。

這樣以來,如果返回的資料過大的話,可能會因為網路的各種問題導致延遲。

但是如果像GraphQL,我要查詢頭像資訊,只需要傳送

{
  hero {
    head_url
  }
}

那麼伺服器就只會返回給

{
  "hero": {
    "head_url": "https://pic.cnblogs.com/face/298986/20150104103009.png"
  }
}

 

嗯,基本就是這樣子。僅僅是我的理解,如果不對,麻煩大聲告訴我,謝謝。

 

官網還有一個gif,更加形象。

 

怎麼樣,可以的吧。

 

3. 現存的GraphQL框架

前面說了,GraphQL僅僅就是一個查詢語言,如果想用在自己的專案中,你不可能從頭開始研究這個語言,然後手擼自己需要的框架。當然,自己寫當然沒問題。

不過官網提供了一些自己寫的或者第三方寫的庫,包括伺服器端和客戶端的實現。

詳見 ↓

英文:https://graphql.org/code/ 

中文:https://graphql.cn/code/

 

這其中不得不讚一下一個第三方的Apollo,牛逼的不要不要的。寫了Android、iOS、Javascript,就是不寫dot net。

.net下不過也有三個:

  • GraphQL.Client: A GraphQL Client for .NET.
  • graphql-net-client: Basic example GraphQL client for .NET.
  • SAHB.GraphQLClient: GraphQL client which supports generating queries from C# classes

 

第一個好像是微軟寫的,261 stars。。。不過距離上一個版本發版已經有一年了,這一年有了一些新的bug,但是他們之說下一個版本解決,但是需要多久???God knows。

第二個是個人開發者寫的,80 stars。但是距離他上一次提交程式碼是三年前了,so。。。放棄吧先

第三個也是個人開發者寫的,26 stars。這個庫更新的比較頻繁,可以使用。

 

4. 一個簡單的例子

拿微軟的例子GraphQL.Client說一下吧

比如就從伺服器獲取一個pin code

 

 首先自己先寫好mutation句子,類似SQL查詢的語句。

@"mutation{
                    generatePinCode
                }"

還是有一點點麻煩的地方,就是自己要寫查詢語句。

像剛才提到的Apollo框架,人家都是給你自動生成的,你氣不氣。

不過有人在Github提問了,但是微軟的人並不打算這麼做,可能他們在忙別的事情吧,比如寫win10的bug

 

 

然後宣告一個GraphQL的Client,指定EndPoint地址。

            GraphQLClient client = new GraphQLClient(new GraphQLClientOptions
            {
                EndPoint = new Uri("http://dev.xxx.com/api/graphql/guest")
            });

然後宣告GraphQLResponse,來接受伺服器返回的訊息。

GraphQLResponse response = await client.PostQueryAsync(
                @"mutation{
                    generatePinCode
                }");

            if(response.Errors == null)
            {
                var result = response.GetDataFieldAs<string>("generatePinCode");
                textBox_Result.Text = textBox_PinCode.Text = result;
            }
            else
                textBox_Result.Text = "Generate Pin Code Failed";

看,就是這麼簡單的例子。

有人可能好奇,上面的generatePinCode是什麼鬼?在哪裡出來的。

其實這個是伺服器返回來的資料,我們需要從generatePinCode資料區拿資料而已。

說到這裡,那麼就不得不說一下Altair這個神器了。

在Altair中,傳送請求,返回來的資料都是包含在data資料體中的。data裡面的generatePinCode才是我們真正想要的。

 

而通過Altair看出,generatePinCode其實返回了就是一個string型別的字串。

那麼我們只需要GetDataFieldAs函式,直接反序列化即可。

 

 

 

 

5. 一個有一點點複雜的例子

拿微軟的例子GraphQL.Client說一下吧

比如需要做個使用者輸入使用者名稱密碼登入的例子:

 

 

那麼我們寫一個簡單的xaml程式碼:

<TextBox x:Name="textBox_Username" PlaceholderText="user name" BorderThickness="1" Margin="0, 20"/>
<TextBox x:Name="textBox_pswd" PlaceholderText="password" BorderThickness="1"/>
<Button Content="Login" Margin="0, 20" Tapped="LoginWithUsernamePassword_Tapped"/>

 

然後自己需要寫mutation程式碼,這裡我們就查詢user 的所有資訊。不過這些資訊可以按需自己獲取。

@"mutation{
                    login(email:""" + textBox_Username.Text 
                    + @""", password:"""+ textBox_pswd.Text 
                    + @"""){
                    access_token
                    token_type
                    expires_in
                    user{id
                        email
                        nickname
                        email_verified_at
                        password
                        remember_token
                        mobile
                        gender
                        birthdate
                        type
                        avatar_uri
                        avatar_radius_uri
                        status
                        auth_privacy
                        account_type
                        created_at
                        updated_at}
                    }
                    }";

 

然後在C#裡面響應 LoginWithUsernamePassword_Tapped 時間。

先宣告一個GraphQL的Client,指定EndPoint地址。

GraphQLClient client = new GraphQLClient(new GraphQLClientOptions
            {
                EndPoint = new Uri("http://dev.xxx.com/api/graphql/guest")
            });

 

然後宣告GraphQLResponse,來接受伺服器返回的訊息。

            GraphQLResponse response = await client.PostQueryAsync(query);
            if (response.Errors == null)
            {
                
            }
            else
                textBox_Result.Text = "Login With Username Password Failed";

 

其實看上面的程式碼,可以看出,和我們之前用Restful Api的方式一模一樣。

1. 向指定的url傳送請求

2. 獲取相應資訊

3. 判斷返回的訊息是否成功,比如status code等。

 

如果GraphQL返回的Response.Errors是空的話,表示查詢成功。接下來要對資料進行反序列化處理,以便接下來我們可以直接使用。

                var result = response.GetDataFieldAs<LoginWithPinCode>("login");
                textBox_Result.Text = result.access_token;
                image_Head.Source = new BitmapImage(new Uri(result.user.avatar_radius_uri));

 

到這裡,可能會有人好奇"login"是怎麼來的?

在Altair中,傳送請求,返回來的資料都是包含在data資料體中的。data裡面的login才是我們真正想要的。

而LoginWithPinCode類是根據返回的login資料,自己定義的model。

 

 

 

好了,到此。一個完整的利用使用者名稱密碼登入的例子就完成了。

C#完整程式碼:

        private async void LoginWithUsernamePassword_Tapped(object sender, TappedRoutedEventArgs e)
        {
            client = new GraphQLClient(new GraphQLClientOptions
            {
                EndPoint = new Uri("http://dev.xxx.com/api/graphql/guest")
            });

            string query = @"mutation{
                    login(email:""" + textBox_Username.Text 
                    + @""", password:"""+ textBox_pswd.Text 
                    + @"""){
                    access_token
                    token_type
                    expires_in
                    user{id
                        email
                        nickname
                        email_verified_at
                        password
                        remember_token
                        mobile
                        gender
                        birthdate
                        type
                        avatar_uri
                        avatar_radius_uri
                        status
                        auth_privacy
                        account_type
                        created_at
                        updated_at}
                    }
                    }";

            GraphQLResponse response = await client.PostQueryAsync(query);

            if (response.Errors == null)
            {
                var result = response.GetDataFieldAs<LoginWithPinCode>("login");
                textBox_Result.Text = result.access_token;
                image_Head.Source = new BitmapImage(new Uri(result.user.avatar_radius_uri));
            }
            else
                textBox_Result.Text = "Login With Username Password Failed";
        }

 

 

6. GraphQL使用總結

如果你的專案突然說要換GraphQL方式查詢之類的,不要慌。沒聽過沒關係,它也是一個api,通過結合了SQL查詢的方式實現。

上面的兩個例子都是用微軟的庫實現的。

如果這三個庫都不能滿足你的要求,那麼就需要用dot net提供的HttpClient來從最底層做起。這樣有個好處就是你可以完全按照自己的需要定製。

像前面提到的Apollo框架,它就存在這樣那種的限制。Android和iOS開發組,在幾個schema檔案上花費了好大一段時間,又是合併檔案又是名稱空間啥的。

不過由於微軟的那個庫(其實也就是封裝了HttpClient,做多了一點處理),封了雖然並沒有那麼的理想,反而避開了schema這一點。

如果你從HttpClient,當然更不會存在這種問題了。

&n