GraphQL 既是一種用於API的查詢語言也是一個知足你數據查詢的運行時(來自:官方解釋)javascript
理解起來就是,GraphQL有本身查詢語法,發起的API請求中經過傳遞查詢語句來告訴服務端須要哪些操做和具體數據字段,GraphQL定義了實現規範,各類的語言分別實現了GraphQL功能框架,經過框架能夠對查詢語法進行解釋執行,而後返回數據輸出給客戶端css
如下全部查詢和輸出都是來自個人DEMO,DEMO的實現和源碼Github地址下面會提到前端
語法特性知足各類需求java
# 查詢語句-有參數 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" } } }
查詢友好性,查詢和輸出關聯react
看查詢語句是否是感受有點兒JSON的味道?查詢語法類JSON格式,先後端均可以很容易上手,查詢語句和輸出數據有緊密的關聯性,經過分析查詢語句就知道輸出的數據內容字段有哪些webpack
靈活性,請求你所要的數據,很少很多ios
能夠自定義查詢語句來獲取須要使用的字段,避免無用字段的輸出,減小沒必要要數據塊/數據字段查詢邏輯git
多字段github
# 查詢語句 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數據查詢web
# 查詢語句 query{ students{ id name } }
# 輸出 { "data": { "students": [ { "id": 19, "name": "Savannah" }, { "id": 34, "name": "Ariana" } ] } }
API演進,無需劃分版本
API版本迭代無須要進行版本號區分,添加字段不影響現有查詢,請求發起者能夠本身定義想要的查詢信息
# Say No http://api.xxx.com/student/v1/ http://api.xxx.com/student/v2/ # ...
自檢性,可查詢輸出全部定義
這個是GraphQL一個很Nice的特性,就是GraphQL服務API能夠經過語句查詢出它所支持的類型,開發能夠不須要花時間寫API文檔,GraphQL直接幫助開發者快速瞭解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-dotnet開源項目裏的GraphiQL要接入本身開發GraphQL接口,還須要進行簡單的修改調整,後面會說到
定義【數據類】MStudent.cs(學生類),MClass.cs(班級類),MResult.cs(執行結果類)
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> /// 總人數 /// </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泛型對應到【數據類】
構造函數裏經過Field去添加能夠被查詢的數據字段,包括:描述以及字段內容獲取的處理方法,等
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 => { //緩存中已經存在就直接返回 if (d.Source.SClass != null) return d.Source.SClass; //從DB/緩存中獲取數據 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("人數"); 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) { //查詢-有參數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對象,根據查詢條件解析執行返回結果輸出
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工具的接入
//調整以下 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'));