溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

GraphQL新手上路

發(fā)布時間:2020-06-29 19:33:39 來源:網(wǎng)絡 閱讀:50638 作者:SFLYQ 欄目:開發(fā)技術

GraphQL新手上路

GraphQL是什么?

GraphQL 既是一種用于API的查詢語言也是一個滿足你數(shù)據(jù)查詢的運行時(來自:官方解釋)

理解起來就是,GraphQL有自己查詢語法,發(fā)起的API請求中通過傳遞查詢語句來告訴服務端需要哪些操作和具體數(shù)據(jù)字段,GraphQL定義了實現(xiàn)規(guī)范,各種的語言分別實現(xiàn)了GraphQL功能框架,通過框架可以對查詢語法進行解釋執(zhí)行,然后返回數(shù)據(jù)輸出給客戶端

GraphQL新手上路


GraphQL的優(yōu)勢

以下所有查詢和輸出都是來自我的DEMO,DEMO的實現(xiàn)和源碼Github地址下面會提到

語法特性滿足各種需求

  • 支持多操作:query->查詢,mutation->修改,規(guī)范是寫在查詢語句前,默認不寫就是query
  • 支持參數(shù),實現(xiàn)各種功能,比如:查詢數(shù)據(jù),排序,分頁,... ...等
  • 語法其他特性,別名,片段,定義變量,指令,... ...等
# 查詢語句-有參數(shù)
query{
  student(id:86){
    id
    name
    sclass{
      id
      num
      level
      heads
    }
  }
}
# 輸出
{
  "data": {
    "student": {
      "id": 86,
      "name": "Emma",
      "sclass": {
        "id": 9,
        "num": 8,
        "level": 3,
        "heads": 68
      }
    }
  }
}
# 修改
mutation {
  update(id: 86, name: "66666") {
    rt
    msg
  }
}
# 輸出
{
  "data": {
    "update": {
      "rt": 1,
      "msg": "bingo"
    }
  }
}

查詢友好性,查詢和輸出關聯(lián)

看查詢語句是不是感覺有點兒JSON的味道?查詢語法類JSON格式,前后端都可以很容易上手,查詢語句和輸出數(shù)據(jù)有緊密的關聯(lián)性,通過分析查詢語句就知道輸出的數(shù)據(jù)內容字段有哪些


靈活性,請求你所要的數(shù)據(jù),不多不少

可以自定義查詢語句來獲取需要使用的字段,避免無用字段的輸出,減少不必要數(shù)據(jù)塊/數(shù)據(jù)字段查詢邏輯

多字段

# 查詢語句
query{
  students{
    id
    name
    classid
    sclass{
      id
      num
      level
      heads
    }
  }
}
# 輸出
{
  "data": {
    "students": [
       {
        "id": 19,
        "name": "Savannah",
        "classid":22,
        "sclass": {
          "id": 22,
          "num": 6,
          "level": 4,
          "heads": 57
        }
      },
      {
        "id": 34,
        "name": "Ariana",
        "classid":33,
        "sclass": {
          "id": 33,
          "num": 3,
          "level": 4,
          "heads": 57
        }
      }
    ]
  }
}

去掉了不使用的字段輸出,少了字段sclass,就可以不進行sclass數(shù)據(jù)查詢

# 查詢語句
query{
  students{
    id
    name
  }
}
# 輸出
{
  "data": {
    "students": [
      {
        "id": 19,
        "name": "Savannah"
      },
      {
        "id": 34,
        "name": "Ariana"
      }
    ]
  }
}

API演進,無需劃分版本

API版本迭代無需要進行版本號區(qū)分,添加字段不影響現(xiàn)有查詢,請求發(fā)起者可以自己定義想要的查詢信息

# Say No
http://api.xxx.com/student/v1/
http://api.xxx.com/student/v2/
# ... 

自檢性,可查詢輸出所有定義

這個是GraphQL一個很Nice的特性,就是GraphQL服務API可以通過語句查詢出它所支持的類型,開發(fā)可以不需要花時間寫API文檔,GraphQL直接幫助開發(fā)者快速了解API。

# 查詢語句
{
  __type(name: "MStudentType") {
    kind
    name
    fields {
      name
      description
      type {
        name
      }
    }
  }
}
# 輸出
{
  "data": {
    "__type": {
      "kind": "OBJECT",
      "name": "MStudentType",
      "fields": [
        {
          "name": "id",
          "description": "學號",
          "type": {
            "name": null
          }
        },
        {
          "name": "name",
          "description": "學生名",
          "type": {
            "name": null
          }
        },
        {
          "name": "age",
          "description": "年齡",
          "type": {
            "name": null
          }
        },
        {
          "name": "birthdate",
          "description": "生日",
          "type": {
            "name": null
          }
        },
        {
          "name": "sclass",
          "description": "班級信息",
          "type": {
            "name": "MClassType"
          }
        }
      ]
    }
  }
}

基于自檢性,GraphQL開源了輔助工具GraphiQL,方便GraphQL接口調試和自動生成接口文檔

  • GraphQL輔助工具:GraphiQL,可以調試查詢語句,并對接口定義的schema進行文檔可視化展示
    • 查詢語句進行感知
    • 錯誤提示
    • 語句格式化
    • 執(zhí)行查詢
    • 查看接口定義的schema文檔信息

graphql-dotnet開源項目里的GraphiQL要接入自己開發(fā)GraphQL接口,還需要進行簡單的修改調整,后面會說到


.NET下的入門教程

  • 構建ASP.NET MVC5 WebAPI 項目
  • NutGet引入程序包
    • GraphQL
    • GenFu,用于初始化測試數(shù)據(jù)
  • 基于GraphQL簡單實現(xiàn)一個學生查詢API
    • 支持查詢學生信息列表
    • 支持查詢學生的班級信息
    • 支持查詢學號對應的學生信息
    • 支持修改學生名字

定義【數(shù)據(jù)類】MStudent.cs(學生類),MClass.cs(班級類),MResult.cs(執(zhí)行結果類)


public class MStudent {
    /// <summary>
    /// 學號
    /// </summary>
    public int Id { get; set; }
    /// <summary>
    /// 名字
    /// </summary>
    public string Name { get; set; }
    /// <summary>
    /// 年齡
    /// </summary>
    public int Age { get; set; }
    /// <summary>
    /// 所在班級編號
    /// </summary>
    public int ClassId { get; set; }
    /// <summary>
    /// 生日
    /// </summary>
    public DateTime Birthdate { get; set; }
    /// <summary>
    /// 班級
    /// </summary>
    public MClass SClass { get; set; }
}

public class MClass {
    public int Id { get; set; }
    /// <summary>
    /// 年級
    /// </summary>
    public int Level { get; set; }
    /// <summary>
    /// 第幾班
    /// </summary>
    public int Num { get; set; }
    /// <summary>
    /// 總人數(shù)
    /// </summary>
    public int Heads { get; set; }
}

public class MResult {
    /// <summary>
    /// 輸出結果,0=失敗,1=成功
    /// </summary>
    public int rt { get; set; }
    /// <summary>
    /// 說明信息
    /// </summary>
    public string msg { get; set; }
}

定義GraphType類 MStudentType,MClassType,MResultType 繼承ObjectGraphType<TSourceType> ,TSourceType泛型對應到【數(shù)據(jù)類】
構造函數(shù)里通過Field去添加可以被查詢的數(shù)據(jù)字段,包括:描述以及字段內容獲取的處理方法,等


public class MStudentType : ObjectGraphType<MStudent> {
    private static BStudent _bll { get; set; }
    public MStudentType() {
        if (_bll == null) _bll = new BStudent();
        Field(d => d.Id).Description("學號");
        Field(d => d.Name).Description("學生名");
        Field(d => d.Age).Description("年齡");
        Field(d => d.Birthdate).Description("生日");
        Field<MClassType>("sclass", resolve: d => {
            //緩存中已經(jīng)存在就直接返回
            if (d.Source.SClass != null) return d.Source.SClass;
            //從DB/緩存中獲取數(shù)據(jù)
            var classId = d.Source?.ClassId ?? 0;
            if (classId > 0) d.Source.SClass = _bll.GetClass(d.Source.ClassId);
            return d.Source.SClass;
        },description:"班級信息");
    }
}

public class MClassType : ObjectGraphType<MClass> {
    public MClassType() {
        Field(d => d.Level).Description("年級");
        Field(d => d.Heads).Description("人數(shù)");
        Field(d => d.Id).Description("編號");
        Field(d => d.Num).Description("班級");
    }
}

public class MResultType : ObjectGraphType<MResult> {
    public MResultType() {
        Field(d => d.rt);
        Field(d => d.msg);
    }
}

定義Schema的操作類(query/mutation),繼承 ObjectGraphType,有:StudentQuery,StudentMutation


public class StudentQuery : ObjectGraphType {
        public StudentQuery(BStudent bll) {
            //查詢-有參數(shù)id
            Field<MStudentType>("student", arguments: new QueryArguments(new QueryArgument<IntGraphType>() {
                Name = "id"
            }), resolve: d => {
                var id = d.Arguments["id"].GetInt(0, false);
                return bll.GetModel(id); ;
            });
            //查詢-列表
            Field<ListGraphType<MStudentType>>("students", resolve: d => {
                return bll.GetStudents();
            });
        }
    }
}

public class StudentMutation : ObjectGraphType {
    public StudentMutation(BStudent bll) {
        Field<MResultType>("update", arguments: new QueryArguments(
            new QueryArgument<IntGraphType> {
                Name = "id"
            },
            new QueryArgument<StringGraphType> {
                Name = "name"
            }
        ), resolve: (d) => {
            var id = d.Arguments["id"].GetInt(0, false);
            var name = d.Arguments["name"].GetString("");
            if (id <= 0) return new MResult {
                rt = 0,
                msg = "非法學號"
            };
            if (name.IsNullOrWhiteSpace()) return new MResult {
                rt = 0,
                msg = "非法名字"
            };
            var isSc = bll.UpdateName(id, name);
            if (!isSc) return new MResult {
                rt = 0,
                msg = "更新失敗"
            };
            return new MResult {
                rt = 1,
                msg = "bingo"
            };
        });
    }
}

在控制器里添加接口,構造Schema對象,根據(jù)查詢條件解析執(zhí)行返回結果輸出
Query = StudentQuery,Mutation = StudentMutation

/// <summary>
/// graphql demo 接口
/// </summary>
/// <returns></returns>
[HttpPost]
[Route("query")]
public object Test_Query() {
    var r = HttpContext.Current.Request;
    var query = r.GetF("query");
    var bll = new BStudent();
    var schema = new Schema { Query = new StudentQuery(bll), Mutation = new StudentMutation(bll) };
    var result = new DocumentExecuter()
        .ExecuteAsync(options => {
            options.Schema = schema;
            options.Query = query;
        }).GetAwaiter();
    var json = new DocumentWriter(indent: true).Write(result);
    return result.GetResult();
}

GraphiQL工具的接入

  • Git Clone graphql-dotnet
  • 安裝NodeJS環(huán)境
  • 命令工具CMD打開graphql-dotnet/src/GraphQL.GraphiQL/ 執(zhí)行下面命令
    • npm install -g yarn
    • yarn install
    • yarn start
  • 運行graphql-dotnet/src/GraphQL.GraphiQL 就可以啟動:http://localhost:47080/
  • 根據(jù)自己的情況調整:graphql-dotnet/src/GraphQL.GraphiQL/app/app.js 腳本,如下面貼出的代碼
    • url=graphql接口地址
    • 如果需要投入生產(chǎn)使用,可以把接口地址進行url傳入,或者支持輸入框輸入地址,不固定接口地址
  • 每次調整完需要重新執(zhí)行 yarn start,前端會使用webpack進行打包操作,執(zhí)行完成后刷新頁面即可

//調整如下
import React from 'react';
import ReactDOM from 'react-dom';
import GraphiQL from 'graphiql';
import axios from 'axios';
import 'graphiql/graphiql.css';
import './app.css';

function graphQLFetcher(graphQLParams) {
    console.log(graphQLParams["query"]);
    return axios({
        method: 'post',
        url: "http://127.0.0.1:5656/query",//window.location.origin + '/api/graphql',
        data: "query=" + graphQLParams["query"],
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
    }).then(resp => resp.data);
}
ReactDOM.render(<GraphiQL fetcher={graphQLFetcher} />, document.getElementById('app'));

GraphQL新手上路

總結

  • 對于應用我覺得可以嘗試使用到新項目需求中,或者現(xiàn)有合適應用場景進行重構,等服務運行穩(wěn)定,并且開發(fā)上手后即可進行大范圍的使用
  • 對于RestFul和GraphQL的比較,我覺得沒有最好的協(xié)議,只有最合適的場景

資源

  • Demo源碼:
    • Demo代碼到我的Gtihub項目(GraphQLDemo)
  • 學習資料
    • 知乎-什么是GraphQL
    • GraphQL語法入門
    • GraphQL中文官網(wǎng)
    • How To GraphQL
    • GraphQL 搭配 Koa 最佳入門實踐

首發(fā)于本人獨立博客

向AI問一下細節(jié)

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

AI