天天看點

讓ASP.NET Core支援GraphQL之-GraphQL的實作原理

衆所周知RESTful API是目前最流行的軟體架構風格之一,它主要用于用戶端和伺服器互動類的軟體。基于這個風格設計的軟體可以更簡潔,更有層次,更易于實作緩存等機制。

RESTFul的優越性是毋庸置疑的,不過GraphQL也可以作為一種補充,讓你的服務既支援RESTFul的http調用,也容許用戶端通過GraphQL支援的聲明式文法調用服務。

本篇文章并不想對比RESTFul和GraphQL孰輕孰重,或者那種方式更好,相關比較可以參考GraphQL的前世今生。本文旨在介紹如何在ASP.NET Core應用中引入GraphQL,讓你的應用既支援RESTFul,也能支援GraphQL。

Web應用程式是如何工作的

如果說一個Service能夠提供一個功能,那麼我們就可以給Service一個輸入,進而得到一個輸出。

讓ASP.NET Core支援GraphQL之-GraphQL的實作原理

如果将若幹個Service組合在一起形成一個應用程式,那麼這個應用程式就可以提供若幹個能力,當一個架構分别就輸入和輸出進行統一的約定和規範時,也就是人們常說的SOAP,RESTful等技術。

讓ASP.NET Core支援GraphQL之-GraphQL的實作原理

對于RESTful來說,輸入就是Http request,輸出是一個json格式的字元串。而Web應用程式架構在做什麼?根據某個輸入(request),找到對應的controller, 擊中合适的action,同時将Request綁定為action方法的參數,最後将結果格式化為json字元串并輸出。

讓ASP.NET Core支援GraphQL之-GraphQL的實作原理

GraphQL就是跟Web架構同一級别的技術,隻不過輸入(input)不再是Http request,而是GraphQL特有的文法結構,輸出仍然為json字元串。

讓ASP.NET Core支援GraphQL之-GraphQL的實作原理

GraphQL能夠做些什麼?

既然GraphQL是一種可以代替RESTful的技術,那麼你一定很想知道他是怎麼做到的。 如果能用一句話總結那就是: GraphQL是一種API資源的查詢語言。GraphQL通過下面的三種類型來滿足使用者的需求:

1. 查詢

我們都知道使用者的請求可以分為兩類:Query和Command,Query用于查詢資源,調用一次和多次都不會影響資源的狀态,一個簡單的查詢如下:

query {
  hero {
    id
    name
  }
}           

上面的查詢語言可以了解為:查詢hero資源的"id"和"name“屬性

2. mutation

所謂mutation就是Command,意味着該使用者請求能夠改變服務端的狀态,一個簡單的mutation如下:

mutation ($human:HumanInput!) {
  createHuman(human: $human) {
    id
    name
  }
}
variables: {
    "human": {
      "name": "Boba Fett",
      "homePlanet": "Kamino"
    }
  }           

上面的mutation可以了解為建立一個humman對象,輸入對象是一個$human變量,最後把建立對象的`”id"和"name"屬性查詢出來。可以看出mutation一般都要配合一個變量使用,變量需要在"variables"中單獨定義。

3. Subscriptions

Subscriptions用于提供類似websocket的功能,GraphQL Server是一個實作了Apollo GraphQL訂閱協定的.NET Core伺服器. 下面的例子需要同時打開兩個浏覽器視窗:

Subscription使用者訂閱聊天消息:

subscription MessageAdded {
  messageAdded {
    from { id displayName }
    content
  }
}           

Mutation使用者添加聊天内容:

mutation AddMessage($message: MessageInputType!) {
  addMessage(message: $message) {
    from {
      id
      displayName
    }
    content
  }
}

variables:
{
  "message": {
    "content": "Message",
    "fromId": "1"
  }
}           

GraphQL是如何實作的

我在用每一個開源架構或者類庫時都習慣于先浏覽源碼,了解整個源碼的大概結構和實作。下面的過程以一個簡單的查詢為例,分析GraphQL的實作原理:

{
  hero {
    name
    height
  }
}           

通過graphQL browser IDE發送請求:

讓ASP.NET Core支援GraphQL之-GraphQL的實作原理

GrpahQL處理的整個過程如下:

1.用戶端将上面的GraphQL query通過http發送到服務端
curl 'http://localhost:5000/graphql' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Connection: keep-alive' -H 'DNT: 1' -H 'Origin: http://localhost:5000' --data-binary '{"query":"# Write your query or mutation here\nquery test{\n  user{\n    age\n  }\n}\n"}' --compressed           
2. 整個Request以json的格式發送到了服務端,服務端将Request反序列化為GraphQLRequest類型:
public class GraphQLRequest
{
    [JsonProperty("query")]
    public string Query { get; set; }
    
    [JsonProperty("variables")]
    public JObject Variables { get; set; }
    
    [JsonProperty("operationName")]
    public string OperationName { get; set; }
    
    public Inputs GetInputs()
    {
      return GraphQLRequest.GetInputs(this.Variables);
    }
}           

針對上面的例子,實際上隻有string Query屬性被反序列化為”"# Write your query or mutation here\nquery test{\n user{\n age\n }\n}\n"“

3.服務端解析Query,解析Query的過程是一個文法分析的過程,通過Paser将Query解析為AST:
var source = new Source(body);
    var result = _parser.Parse(source);           

Parse後的結果是一個Document類:

public class Document : AbstractNode
{
    public string OriginalQuery { get; set; }
    public Operations Operations { get; }
    public Fragments Fragments { get; }
}           

本例的Query将會被解析為一個Operations,一個Operations将包含若幹個有層次結構的Operation,解析Query的目的是為了知道用戶端要查詢hero.Name和hero.Height兩個屬性。

4.有了一個Parse後的Document,接下來的工作将有DocumentExecuter來完成,DocumentExecuter定義了整個調用服務端資源的流程:
public async Task<ExecutionResult> ExecuteAsync(ExecutionOptions options)
{
    //1. 列印開始時間
    //2. Parse Document
    //3. 驗證Document是否是一個合法的GrapQL文法請求
    //4. 在流程的各個階段執行Listener,用于在不同的時機切入代碼,類似于ASP.NET Core中的Filter
    //5. 選擇合适的執行政策
    //6. 執行服務端資源
    //7. 輸出Response
}
           

以上就是GraphQL在.NET Core中的實作原理分析,下一篇将通過一個hello world級别的例子示範如何讓你的ASP.NET應用程式支援GraphQL.

繼續閱讀