Entah bagaimana saya mendapat beberapa hari libur, dan saya membuat sketsa server GraphQL ke platform Docsvision kami. Di bawah ini saya akan memberi tahu Anda bagaimana hasilnya.

Platform Docsvision mencakup banyak alat berbeda untuk membangun sistem alur kerja, tetapi komponen utamanya adalah seperti ORM. Ada editor metadata di mana Anda bisa menggambarkan struktur bidang kartu. Mungkin ada bagian struktural, pengumpulan dan pohon, yang, apalagi, dapat disarangkan, secara umum, semuanya rumit . Database dihasilkan oleh metadata, dan kemudian Anda bisa bekerja dengannya melalui beberapa C # API. Singkatnya - opsi ideal untuk membangun server GraphQL.
Apa saja opsinya
Jujur saja, tidak banyak pilihan dan mereka biasa-biasa saja. Saya berhasil menemukan hanya dua perpustakaan:
UPD: dalam komentar mereka menyarankan bahwa masih ada Hotchocolate .
Pada README, pada awalnya saya menyukai yang kedua, dan saya bahkan mulai melakukan sesuatu dengannya. Tapi dia segera mengetahui bahwa API-nya terlalu buruk, dan dia tidak bisa mengatasi tugas menghasilkan skema metadata. Namun, tampaknya sudah ditinggalkan (komitmen terakhir setahun yang lalu).
API graphql-dotnet
cukup fleksibel, tetapi pada saat yang sama sangat didokumentasikan, membingungkan, dan tidak intuitif. Untuk memahami cara bekerja dengannya, saya harus melihat kode sumber ... Benar, saya bekerja dengan versi 0.16
, sementara sekarang yang terakhir adalah 0.17.3
, dan 7 versi beta 2.0
telah dirilis. Jadi saya minta maaf jika bahannya agak ketinggalan jaman.
Saya juga harus menyebutkan bahwa perpustakaan datang dengan rakitan yang tidak ditandatangani. Saya harus membangunnya kembali dari sumber secara manual untuk menggunakannya dalam aplikasi ASP.NET kami dengan rakitan yang ditandatangani.
Struktur server GraphQL
Jika Anda tidak terbiasa dengan GraphQL, Anda dapat mencoba github explorer . Sebuah rahasia kecil - Anda dapat menekan Ctrl + space untuk mendapatkan pelengkapan otomatis. Bagian klien tidak lebih dari GraphiQL , yang dapat dengan mudah dipasangkan ke server Anda. Ambil saja index.html , tambahkan skrip dari paket npm, dan ubah url di fungsi graphQLFetcher ke alamat server Anda - hanya itu, Anda dapat memainkannya.
Pertimbangkan permintaan sederhana:
query { viewer { login, company } }
Di sini kita melihat seperangkat bidang - viewer, di dalamnya masuk, perusahaan. Tugas kita, seperti backend GraphQL, adalah untuk membangun di server beberapa "skema" di mana semua bidang ini akan diproses. Bahkan, kita hanya perlu membuat struktur objek layanan yang sesuai dengan deskripsi bidang, dan mendefinisikan fungsi panggil balik untuk menghitung nilai.
Skema ini dapat dibuat secara otomatis berdasarkan kelas C # , tetapi kita akan melalui hardcore - kita akan melakukan segalanya dengan tangan kita. Tapi ini bukan karena saya orang yang gagah, hanya menghasilkan skema berbasis metadata adalah skrip non-standar di graphql-dotnet yang tidak didukung oleh dokumentasi resmi. Jadi, kami menggali sedikit di perutnya, di daerah tanpa dokumen.
Setelah membuat skema, tetap bagi kami untuk mengirimkan string permintaan (dan parameter) dari klien ke server dengan cara yang mudah (tidak masalah bagaimana GET, POST, SignalR, TCP ...), dan memberi makan mesinnya bersama dengan skema. Mesin akan memuntahkan objek dengan hasil yang kita ubah menjadi JSON dan mengembalikannya ke klien. Ini terlihat seperti ini untuk saya:
// , var schema = GraphQlService.GetCardsSchema(sessionContext); // ( ) var executer = new DocumentExecuter(); // , var dict = await executer.ExecuteAsync(schema, sessionContext, request.Query, request.MethodName).ConfigureAwait(false); // - :) if (dict.Errors != null && dict.Errors.Count > 0) { throw new InvalidOperationException(dict.Errors.First().Message); } // return Json(dict.Data);
Anda dapat memperhatikan sessionContext
. Ini adalah objek khusus Documents kami yang melaluinya platform diakses. Saat membuat skema, kami selalu bekerja dengan konteks tertentu, tetapi lebih pada nanti.
Generasi sirkuit
Semuanya dimulai dengan cara yang menyentuh:
Schema schema = new Schema();
Sayangnya, disinilah kode sederhana berakhir. Untuk menambahkan bidang ke skema, kita perlu:
- Jelaskan tipenya - buat ObjectGraphType, StringGraphType, BooleanGraphType, IdGraphType, IntGraphType, DateGraphType atau objek FloatGraphType.
- Jelaskan bidang itu sendiri (nama, penangan) - buat objek GraphQL.Types.FieldType
Mari kita coba gambarkan permintaan sederhana yang saya kutip di atas. Dalam permintaan, kami memiliki satu penampil lapangan. Untuk menambahkannya ke kueri, Anda harus terlebih dahulu menjelaskan tipenya. Jenisnya sederhana - sebuah objek, dengan dua bidang string - login dan perusahaan. Kami menggambarkan bidang login:
var loginField = new GraphQL.Types.FieldType(); loginField.Name = "login"; loginField.ResolvedType = new StringGraphType(); loginField.Type = typeof(string); loginField.Resolver = new MyViewerLoginResolver();
Kami membuat objek companyField dengan cara yang sama - luar biasa, kami siap menjelaskan jenis bidang pemirsa.
ObjectGraphType<UserInfo> viewerType = new ObjectGraphType<UserInfo>(); viewerType.Name = "Viewer"; viewerType.AddField(loginField); viewerType.AddField(companyField);
Ada tipe, sekarang kita bisa menggambarkan bidang pemirsa itu sendiri:
var viewerField = new GraphQL.Types.FieldType(); viewerField.Name = "viewer"; viewerField.ResolvedType = viewerType; viewerField.Type = typeof(UserInfo); viewerField.Resolver = new MyViewerResolver();
Nah, dan sentuhan terakhir, tambahkan bidang kami ke jenis kueri:
var queryType = new ObjectGraphType(); queryType.AddField(viewerField); schema.Query = queryType;
Itu saja, skema kami sudah siap.
Koleksi, pagination, pemrosesan parameter
Jika bidang mengembalikan bukan satu objek, tetapi koleksi, maka Anda perlu menentukan ini secara eksplisit. Untuk melakukan ini, cukup bungkus tipe properti dalam instance dari kelas ListGraphType. Misalkan jika pemirsa mengembalikan koleksi, kami hanya akan menulis ini:
Dengan demikian, dalam resolver MyViewerResolver maka akan diperlukan untuk mengembalikan daftar.
Saat bidang koleksi muncul, penting untuk segera menangani paging. Tidak ada mekanisme siap pakai di sini, semuanya dilakukan melalui parameter . Anda dapat melihat contoh penggunaan parameter pada contoh di atas (cardDocument memiliki parameter id). Mari kita tambahkan parameter seperti itu ke pemirsa:
var idArgument = new QueryArgument(typeof(IdGraphType)); idArgument.Name = "id"; idArgument.ResolvedType = new IdGraphType(); idArgument.DefaultValue = Guid.Empty; viewerField.Arguments = new QueryArguments(idArgument);
Maka Anda bisa mendapatkan nilai parameter di resolver seperti ini:
public object Resolve(ResolveFieldContext context) { var idArgStr = context.Arguments?["id"].ToString() ?? Guid.Empty.ToString(); var idArg = Guid.Parse(idArgStr);
GraphQL diketik sehingga Guid, tentu saja, tidak dapat menguraikan. Oh well, itu tidak sulit bagi kami.
Permintaan Kartu Docsvision
Dalam implementasi GrapqhQL untuk platform Docsvision, saya hanya perlu membaca kode metadata ( sessionContext.Session.CardManager.CardTypes
), dan untuk semua kartu dan bagian-bagiannya, saya secara otomatis membuat objek dengan resolusi yang sesuai. Hasilnya kira-kira seperti ini:
query { cardDocument(id: "{AF652E55-7BCF-E711-8308-54A05079B7BF}") { mainInfo { name instanceID } } }
Di sini cardDocument adalah jenis kartu, mainInfo adalah nama bagian di dalamnya, nama dan instanceID adalah bidang di bagian tersebut. Penyelesai yang sesuai untuk kartu, bagian, dan bidang menggunakan API CardManager sebagai berikut:
class CardDataResolver : GraphQL.Resolvers.IFieldResolver { public object Resolve(ResolveFieldContext context) { var sessionContext = (context.Source as SessionContext); var idArg = Guid.Parse(context.Arguments?["id"].ToString() ?? Guid.Empty.ToString()); return sessionContext.Session.CardManager.GetCardData(idArg); } } class SectionResolver : GraphQL.Resolvers.IFieldResolver { CardSection section; public SectionFieldResolver(CardSection section) { this.section = section; } public object Resolve(ResolveFieldContext context) { var idArg = Guid.Parse(context.Arguments?["id"].ToString() ?? Guid.Empty.ToString()); var skipArg = (int?)context.Arguments?["skip"] ?? 0; var takeArg = (int?)context.Arguments?["take"] ?? 15; var sectionData = (context.Source as CardData).Sections[section.Id]; return idArg == Guid.Empty ? sectionData.GetAllRows().Skip(skipArg).Take(takeArg) : new List<RowData> { sectionData.GetRow(idArg) }; } } class RowFieldResolver : GraphQL.Resolvers.IFieldResolver { Field field; public RowFieldResolver(Field field) { this.field = field; } public object Resolve(ResolveFieldContext context) { return (context.Source as RowData)[field.Alias]; } }
Tentu saja, di sini Anda hanya dapat meminta kartu melalui id, tetapi mudah untuk membuat skema dengan cara yang sama untuk mengakses laporan, layanan, dan hal-hal lain yang canggih. Dengan API ini, Anda bisa mendapatkan data apa pun dari basis data Docsvision hanya dengan menulis JavaScript yang sesuai - sangat mudah untuk menulis skrip dan ekstensi Anda sendiri.
Kesimpulan
Dengan GrapqhQL di .NET, semuanya tidak mudah. Ada satu perpustakaan yang agak hidup, tanpa vendor yang andal dan dengan masa depan yang tidak dapat dipahami, API yang tidak stabil dan aneh, yang tidak diketahui bagaimana berperilaku di bawah beban dan seberapa stabilnya. Tetapi kami memiliki apa yang kami miliki, tampaknya berhasil, tetapi kekurangan dalam dokumentasi dan sisanya diimbangi oleh keterbukaan kode sumber.
Apa yang saya jelaskan dalam artikel ini adalah API yang semakin tidak berdokumen, yang saya eksplorasi dengan mengetik dan mempelajari sumbernya. Hanya saja para penulis perpustakaan tidak berpikir bahwa seseorang akan perlu membuat sirkuit secara otomatis - yah, apa yang bisa Anda lakukan, ini adalah open source.
Itu ditulis sepanjang akhir pekan ini, dan dengan sendirinya, sejauh ini tidak lebih dari sebuah prototipe. Dalam paket Docsvision standar, ini mungkin muncul, tetapi ketika - masih sulit untuk mengatakan. Namun, jika Anda menyukai gagasan mengakses database Docsvision langsung dari JavaScrpit tanpa menulis ekstensi server, tulis. Semakin tinggi minat dari mitra, semakin banyak perhatian yang akan kami curahkan untuk ini.