Hubungan antara C # dan C #: REST, gRPC dan semua yang ada di antaranya

Ada banyak cara untuk berkomunikasi antara klien C # dan server C #. Beberapa dari mereka dapat diandalkan, yang lain tidak. Beberapa sangat cepat, yang lain tidak. Penting untuk mengetahui berbagai opsi sehingga Anda dapat memutuskan mana yang terbaik untuk Anda. Artikel ini akan membahas teknologi paling populer saat ini dan mengapa mereka begitu banyak digunakan. Kami akan berbicara tentang REST, gRPC dan semua yang ada di antaranya.

Skenario optimal


Mari kita lihat bagaimana kita ingin komunikasi client-server kita terlihat di dunia nyata. Saya menyajikan sesuatu seperti ini:

// on client side
public void Foo()
{
    var server = new MyServer(new Uri("https://www.myserver.com/");)
    int sum = server.Calculator.SumNumbers(12,13); 
}

// on server side
class CalculatorController : Controller{
    public int SumNumbers(int a, int b)
    {
        return a + b;
    }
}

Saya ingin mendapat dukungan penuh Intellisense. Ketika saya mengklik server dan . Saya ingin Visual Studio menunjukkan semua pengontrol. Dan ketika saya mengklik CalculatorController dan . Saya ingin melihat semua metode tindakan. Saya juga menginginkan kinerja tertinggi, beban jaringan sangat rendah, dan transfer data dua arah. Dan saya membutuhkan sistem yang andal yang mengatasi kontrol versi sehingga saya dapat dengan mudah menyebarkan versi baru klien dan versi baru server.

Ini terlalu banyak?

Perhatikan bahwa saya berbicara tentang API tanpa kewarganegaraan di sini . Ini sama dengan proyek C # di mana hanya ada dua jenis kelas:

  • Kelas statis dengan metode statis saja.
  • Kelas POCO di mana tidak ada apa pun selain bidang dan properti yang jenisnya adalah kelas POCO primitif atau lainnya.

Memiliki status dalam API memperkenalkan kompleksitas, dan ini adalah akar dari semua kejahatan. Jadi, demi artikel ini, mari kita buat hal-hal yang indah dan tanpa kewarganegaraan.

Pendekatan REST tradisional


REST API muncul pada awal 2000-an dan menaklukkan Internet. Sekarang ini adalah cara paling populer untuk membuat layanan web.

REST mendefinisikan set tetap operasi GET , POST , PUT, dan DELETE untuk permintaan dari klien ke server. Untuk setiap permintaan, kami mendapat respons berisi muatan (biasanya JSON). Permintaan menyertakan parameter dalam permintaan itu sendiri atau sebagai muatan (biasanya JSON) saat permintaan POST atau PUT.

Ada standar API tenang yang mendefinisikan aturan-aturan berikut (yang Anda tidak benar-benar butuhkan):

  • GET digunakan untuk mendapatkan sumber daya
  • PUT digunakan untuk mengubah status sumber daya.
  • POST digunakan untuk membuat sumber daya.
  • HAPUS digunakan untuk menghapus sumber daya.

Jika Anda belum terbiasa dengan REST sejauh ini, penjelasan di atas mungkin tidak akan membantu, jadi inilah contohnya. .NET memiliki dukungan REST bawaan. Bahkan, ASP.NET Web API secara default adalah layanan web REST. Inilah tampilan klien dan server ASP.NET yang khas:

Di server:

[Route("People")]
public class PeopleController : Controller
{
    [HttpGet]
    public Person GetPersonById(int id)
    {
        Person person = _db.GetPerson(id);
        return person;//Automatically serialized to JSON
    }
} 

Pada klien:

var client = new HttpClient();
string resultJson = await client.GetStringAsync("https://www.myserver.com/People/GetPersonById?id=123");
Person person = JsonConvert.DeserializeObject<Person>(resultJson);

REST sangat nyaman, tetapi tidak cocok untuk skenario optimal. Jadi mari kita lihat apakah kita bisa melakukannya dengan lebih baik.

Mereparasi


ReFit bukan alternatif untuk REST. Alih-alih, itu dibangun di atas REST dan memungkinkan kita untuk memanggil titik akhir server seolah-olah mereka adalah metode sederhana. Ini dicapai dengan berbagi antarmuka antara klien dan server. Di sisi server, pengontrol Anda akan mengimplementasikan antarmuka:

public interface IMyEmployeeApi
{
    [Get("/employee/{id}")]
    Task<Employee> GetEmployee(string id);
}

Kemudian di sisi klien Anda harus mengaktifkan antarmuka yang sama dan menggunakan kode berikut:


var api = RestService.For<IMyEmployeeApi>("https://www.myserver.com");
var employee = await api.GetEmployee("abc");

Ini sangat sederhana. Tidak perlu menjalankan otomatisasi kompleks atau menggunakan alat pihak ketiga, kecuali untuk beberapa paket NuGet.

Ini semakin mendekati skenario optimal. Sekarang kami memiliki IntelliSense dan kontrak yang andal antara klien dan server. Tetapi ada opsi lain yang bahkan lebih baik dalam beberapa hal.

Kesombongan


Seperti ReFit, Swagger juga dibangun di atas REST. OpenAPI , atau Swagger , adalah spesifikasi REST API. Ini menggambarkan layanan web REST dalam file JSON sederhana. File-file ini adalah skema API layanan web. Mereka termasuk:

  • Semua jalur (URL) di API.
  • Operasi yang diharapkan (GET, POST, ...) untuk setiap jalur. Setiap jalur dapat menangani operasi yang berbeda. Misalnya, jalur yang sama mystore.com/Product dapat menerima operasi POST yang menambahkan produk dan operasi GET yang mengembalikan produk.
  • Parameter yang diharapkan untuk setiap jalur dan operasi.
  • Jawaban yang diharapkan untuk setiap jalur.
  • Jenis setiap parameter dan objek respons.

File JSON ini pada dasarnya adalah kontrak antara klien dan server. Berikut adalah contoh file kesombongan yang menggambarkan layanan web yang disebut Swagger Petstore (untuk kejelasan, saya menghapus beberapa bagian):

Skema Json
{ 
   "swagger":"2.0",
   "info":{ 
      "version":"1.0.0",
      "title":"Swagger Petstore",
      "description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification",
   },
   "host":"petstore.swagger.io",
   "basePath":"/api",
   "schemes":[ 
      "http"
   ],
   "consumes":[ 
      "application/json"
   ],
   "produces":[ 
      "application/json"
   ],
   "paths":{ 
      "/pets":{ 
         "get":{ 
            "description":"Returns all pets from the system that the user has access to",
            "operationId":"findPets",
            "produces":[ 
               "application/json",
               "application/xml",
            ],
            "parameters":[ 
               { 
                  "name":"tags",
                  "in":"query",
                  "description":"tags to filter by",
                  "required":false,
                  "type":"array",
                  "items":{ 
                     "type":"string"
                  },
                  "collectionFormat":"csv"
               },
               { 
                  "name":"limit",
                  "in":"query",
                  "description":"maximum number of results to return",
                  "required":false,
                  "type":"integer",
                  "format":"int32"
               }
            ],
            "responses":{ 
               "200":{ 
                  "description":"pet response",
                  "schema":{ 
                     "type":"array",
                     "items":{ 
                        "$ref":"#/definitions/Pet"
                     }
                  }
               },
...
 


Mari kita lihat konsekuensi dari ini. Menggunakan file JSON seperti di atas, Anda berpotensi membuat klien C # dengan IntelliSense penuh. Pada akhirnya, Anda tahu semua jalur, operasi, parameter apa yang mereka harapkan, apa jenis parameter, apa jawabannya.

Ada beberapa alat yang melakukan hal itu. Di sisi server, Anda dapat menggunakan Swashbuckle.AspNetCore untuk menambahkan Swagger ke ASP.NET Anda dan membuat file JSON yang ditentukan. Untuk sisi klien, Anda dapat menggunakan swagger -codegen dan AutoRest untuk memproses file-file ini dalam format JSON dan menghasilkan klien. Mari kita lihat contoh bagaimana melakukan ini:

Menambahkan Swagger ke ASP.NET Server Anda


Mulailah dengan menambahkan paket NuGet Swashbuckle.AspNetCore . Di ConfigureServices , daftarkan generator Swagger:

services.AddSwaggerGen(options => 
	options.SwaggerDoc("v1", new OpenApiInfo {Title = "My Web API", Version = "v1"}));

Dalam file Startup.cs dalam metode Konfigurasi , tambahkan:

app.UseSwagger();

Akhirnya, metode aksi di dalam controller harus ditandai dengan atribut [HttpXXX] dan [FromXXX] :

[HttpPost]
public async Task AddEmployee([FromBody]Employee employee)
{
    //...
}
 
[HttpGet]
public async Task<Employee> Employee([FromQuery]string id)
{
    //...
}

Sangat sederhana untuk sisi server. Ketika proyek dimulai , file swagger.json akan dihasilkan , yang dapat Anda gunakan untuk menghasilkan klien.

Menghasilkan klien dari Swagger menggunakan AutoRest


Untuk mulai menggunakan AutoRest , instal dengan npm : npm install -g autorest . Setelah instalasi, Anda harus menggunakan antarmuka baris perintah AutoRest untuk membuat klien C # dari file swagger.json . Berikut ini sebuah contoh:

autorest --input-file="./swagger.json" --output-folder="GeneratedClient" --namespace="MyClient" --override-client-name="MyClient" --csharp

Ini akan membuat folder GeneratedClient dengan file C # yang dihasilkan. Perhatikan bahwa namespace dan nama klien didefinisikan ulang. Tambahkan folder ini ke proyek klien Anda di Visual Studio seperti yang ditunjukkan di bawah ini.



Anda perlu menginstal paket NuGet Microsoft.Rest.ClientRuntime , karena kode yang dihasilkan tergantung padanya. Setelah instalasi, Anda dapat menggunakan API seperti kelas C # biasa:

var client = new MyClient();
Employee employee = client.Employee(id: "abc");

Ada beberapa seluk-beluk yang dapat Anda baca di dokumentasi AutoRest. Dan Anda perlu mengotomatiskan proses ini, jadi saya sarankan membaca manual Patrick Svensson untuk beberapa tips yang berguna, serta artikel ini oleh Peter Yausovets.

Masalah saya dengan Swagger adalah bahwa file JSON dibuat pada saat runtime, jadi ini membuatnya sedikit sulit untuk mengotomatiskan proses CI / CD.

REST tradisional vs Swagger vs ReFit


Berikut adalah beberapa hal yang perlu dipertimbangkan ketika memilih.

  • Jika Anda memiliki REST API pribadi yang sangat sederhana, Anda mungkin tidak perlu khawatir tentang pembuatan klien dan antarmuka umum. Tugas kecil tidak membenarkan upaya tambahan.
  • Swagger mendukung banyak bahasa, dan ReFit hanya mendukung .NET. Kesombongan juga menjadi dasar bagi banyak alat, pengujian, alat otomatisasi, dan alat antarmuka pengguna. Ini mungkin akan menjadi pilihan terbaik jika Anda membuat API publik yang besar.
  • Kesombongan jauh lebih rumit daripada ReFit. Dengan ReFit, itu hanya masalah menambahkan antarmuka tunggal untuk proyek server dan klien Anda. Di sisi lain, dengan ReFit Anda harus membuat antarmuka baru untuk setiap pengontrol, sementara Swagger akan mengurus ini secara otomatis.

Tetapi sebelum Anda memutuskan sesuatu, periksa opsi ke-4, yang tidak ada hubungannya dengan REST.

gRPC


gRPC (gRPC - Remote Procedure Call) adalah sistem panggilan prosedur jarak jauh open source yang dikembangkan oleh Google. Ini agak seperti REST dalam arti menyediakan cara untuk mengirim permintaan dari klien ke server. Tapi ini sangat berbeda, berikut persamaan dan perbedaannya:

  • Seperti REST, gRPC independen terhadap bahasa. Ada alat untuk semua bahasa populer, termasuk C #.
  • gRPC berbasis kontrak dan menggunakan file .proto untuk mendefinisikan kontrak. Ini agak mirip dengan Swagger swagger.json dan antarmuka ReFit yang umum. Klien dari bahasa pemrograman apa pun dapat dihasilkan dari file-file ini.
  • gRPC Protocol Buffer (Protobuf). REST, JSON XML. , , .
  • gRPC HTTP/2. . , REST HTTP 1.x ( HTTP 1.1).
  • HTTP 1.1 TCP- () , HTTP/2 .
  • HTTP/2 . , TCP- . , , HTTP 1.1.
  • gRPC .

Ada dua cara untuk menggunakan gRPC. Untuk .NET Core 3.0, ada gRPC yang sepenuhnya dikelola untuk .NET library . Anda juga dapat menggunakan gRPC C # . Ini tidak berarti bahwa gRPC untuk .NET menggantikan gRPC C # . Mari kita lihat contoh dengan gRPC yang lebih baru untuk .NET .

GRPC untuk sisi server .NET


Ini bukan panduan, melainkan gagasan umum tentang apa yang diharapkan. Inilah contoh contoh pengontrol di gRPC:


public class GreeterService : Greeter.GreeterBase
{
    public override Task<HelloReply> SayHello(HelloRequest request,
        ServerCallContext context)
    {
        _logger.LogInformation("Saying hello to {Name}", request.Name);
        return Task.FromResult(new HelloReply 
        {
            Message = "Hello " + request.Name
        });
    }
}

Anda perlu menambahkan yang berikut ini ke Konfigurasi di file Startup.cs :


app.UseEndpoints(endpoints =>
{
    endpoints.MapGrpcService<GreeterService>();
});

API dijelaskan dalam file .proto , yang merupakan bagian dari proyek:

syntax = "proto3";
 
service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}
 
message HelloRequest {
  string name = 1;
}
 
message HelloReply {
  string message = 1;
}

Ini .proto file ditambahkan dalam Csproj file:

<ItemGroup>
  <Protobuf Include="Protos\greet.proto"/>
</ItemGroup>

GRPC untuk sisi klien NET


Klien dihasilkan dari file .proto . Kode itu sendiri sangat sederhana:


var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);
 
var response = await client.SayHello(
    new HelloRequest { Name = "World" });
 
Console.WriteLine(response.Message);

gRPC vs REST


GRPC terdengar seperti kesepakatan yang bagus. Lebih cepat dan lebih mudah di bawah tenda. Jadi haruskah kita semua beralih dari REST ke gRPC? Jawabannya tergantung . Berikut adalah beberapa pertimbangan:

Dalam pengalaman saya, bekerja dengan gRPC dan ASP.NET masih kecil. Anda akan lebih baik dengan dukungan REST yang matang. Sejauh menyangkut komunikasi kontrak, ini bagus, kecuali bahwa Anda memiliki alternatif yang mirip dengan REST yang telah kita bicarakan: Kesombongan dan ReFit.

Keuntungan terbesar adalah kinerja. Dalam kebanyakan kasus, menurut kriteria ini , gRPC jauh lebih cepat. Terutama untuk muatan besar yang serialisasi Protobuf benar-benar penting. Ini berarti bahwa ini adalah keuntungan yang sangat besar untuk server dengan beban tinggi.

Pindah dari REST ke gRPC dalam aplikasi ASP.NET besar akan sulit. Namun, jika Anda memiliki arsitektur berbasis layanan mikro, maka transisi ini akan menjadi jauh lebih mudah untuk diselesaikan secara bertahap.

Cara komunikasi lainnya


Ada beberapa cara komunikasi yang tidak saya sebutkan sama sekali, tetapi perlu diketahui bahwa mereka ada:

  • GraphQL adalah bahasa permintaan API yang dikembangkan oleh Facebook. Ini memungkinkan klien untuk meminta data yang dibutuhkan dari server. Dengan demikian, Anda dapat membuat hanya satu titik akhir di server, yang akan sangat fleksibel dan hanya akan mengembalikan data yang dibutuhkan klien. GraphQL telah menjadi sangat populer dalam beberapa tahun terakhir.
  • TcpClient TcpListener ( System.Net.Sockets) TCP. , . , ASP.NET API.
  • UdpClient UDP. TCP , UDP . TCP , , UDP β€” . UDP , , . : , IP (VoIP).
  • WCF adalah teknologi lama yang terutama menggunakan komunikasi antar-proses berbasis SOAP. Ini adalah kerangka kerja besar yang tidak akan saya bahas, saya hanya bisa mengatakan bahwa itu telah kehilangan popularitasnya untuk muatan REST dan JSON.

Itu saja. Saya harap artikelnya menarik! Kode yang bagus untuk semua!

All Articles