.Net Core gRpc調用

来源:https://www.cnblogs.com/qfccc/archive/2023/08/09/17617999.html
-Advertisement-
Play Games

[toc] # 簡介 - 高性能、開源的通用 RPC 框架 - 實現不同語言相互調用 - [官網](https://grpc.io/) - [Protobuf 消息參考](https://learn.microsoft.com/zh-cn/aspnet/core/grpc/protobuf?view ...


目錄

簡介

創建gRPC

創建服務端

  1. vs2022直接搜索grpc預設下一步創建

創建控制台測試

  1. 創建控制台
  2. 引入以下dll
<PackageReference Include="Google.Protobuf" Version="3.23.4" />
<PackageReference Include="Grpc.Net.Client" Version="2.55.0" />
<PackageReference Include="Grpc.Tools" Version="2.56.2">
  1. 打開服務端.csproj文件,複製以下內容粘貼到客戶端的.csproj文件中並修改GrpcServices=Client,客戶端.csproj文件出現 None Update="Protos\greet.proto" 這組ItemGroup是可以刪除的
<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>

4.將服務端Protos文件夾及內容全部拷貝到客戶端項目下
5.在客戶端創建gRpcRequest.cs文件並增加下列代碼,埠號填寫服務端埠

using Grpc.Net.Client;
using GrpcService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static GrpcService.Greeter;

namespace gRpcConsoleTest
{
    public static class gRpcRequest
    {
        public static async Task SayHello()
        {
            using(var channel = GrpcChannel.ForAddress("https://localhost:7166")) 
            {
                GreeterClient client = new GreeterClient(channel);
                HelloReply reply = await client.SayHelloAsync(new HelloRequest() {Name = "jjjjj" });
                Console.WriteLine(reply.Message);
            }
        }
    }
}

6.客戶端Program.cs文件中加入測試代碼 await gRpcRequest.SayHello();
7.服務端和客戶端在想要調試的位置打上斷點
8.運行服務端
9.選中客戶端項目右鍵 -> 調試 -> 啟動新實例 即可兩個項目全部命中斷點進行調試測試

創建自定義服務

  • 服務端

    1. Protos文件夾添加 custom.proto文件並添加下列代碼,重新生成項目打開項目所在文件夾,打開路徑:obj\Debug\net6.0\Protos 查看CustomGrpc.cs是否存在,如果沒有存在則在.csproj文件中添加:
    syntax = "proto3";
    
    option csharp_namespace = "Custom.Service";
    
    package custom;
    
    service CustomGreeter {
      rpc Plus(Number) returns (NumberResult) ;
    }
    
    message Number {
        int32 leftNumber = 1;
        int32 rightNumber = 2;
    }  
    
    message NumberResult{
        int32 result = 1;
    }
    
    1. Services文件夾下添加CustomGreeterService.cs文件,namespace 與 .protos中的csharp_namespace對應
    using Grpc.Core;
    
    namespace Custom.Service
    {
        public class CustomGreeterService : CustomGreeter.CustomGreeterBase
        {
            public override async Task<NumberResult> Plus(Number request, ServerCallContext context) => 
                await Task.FromResult<NumberResult>(new NumberResult()
                {
                    Result = request.LeftNumber + request.RightNumber,
                });
        }
    }
    

    3.在Program.cs 中註冊新創建的服務,加入下列代碼:
    app.MapGrpcService();

  • 客戶端

    1. 將服務端的custom.proto文件拷貝到Protos文件夾內併在.csproj文件中添加(註意這裡GrpcServices="Client"):
    2. 重新生成項目並檢查路徑: obj\Debug\net6.0\Protos 是否生成對應的CustomGrpc.cs文件
    3. 加入測試代碼:
    public static async Task Plus(int leftNumber,int rightNumber)
    {
        using (var channel = GrpcChannel.ForAddress("https://localhost:7166"))
        {
            CustomGreeterClient client = new CustomGreeterClient(channel);
            NumberResult number = await client.PlusAsync(new Custom.Service.Number() { LeftNumber = leftNumber,RightNumber = rightNumber});
            Console.WriteLine(number.Result);
        }
    }
    
    1. 測試步驟與上面控制台測試一樣,調試方式也一樣

伺服器流式處理方法

custom.proto

syntax = "proto3";

option csharp_namespace = "Custom.Service";

package custom;

service CustomGreeter {
  rpc SelfIncreaseServer(IntArrayModel) returns (stream BathTheCatResp); //服務端流
}

message BathTheCatResp{
    string message = 1;
}

message IntArrayModel{
    repeated int32 number = 1;
}

CustomGreeterService.cs

using Grpc.Core;

namespace Custom.Service
{
    public class CustomGreeterService : CustomGreeter.CustomGreeterBase
    {
        public override async Task SelfIncreaseServer(IntArrayModel request, IServerStreamWriter<BathTheCatResp> responseStream, ServerCallContext context)
        {
            foreach (var item in request.Number)
            {
                Console.WriteLine($"客戶端傳入參數: {item}");
                await responseStream.WriteAsync(new BathTheCatResp() { Message = item.ToString()});
                await Task.Delay(1000);
            }
        }
    }
}

gRpcRequest.cs

using Custom.Service;
using Grpc.Core;
using Grpc.Net.Client;
using GrpcService;
using static Custom.Service.CustomGreeter;

namespace gRpcConsoleTest
{
    public static class gRpcRequest
    {
        public static async Task SelfIncreaseServe()
        {
            using (var channel = GrpcChannel.ForAddress("https://localhost:7166"))
            {
                CustomGreeterClient client = new CustomGreeterClient(channel);
                IntArrayModel intArray = new IntArrayModel();
                for (int i = 0; i < 10; i++)
                {
                    intArray.Number.Add(i);   
                }

                var batch = client.SelfIncreaseServer(intArray);

                Task batchTask = Task.Run(async ()=>
                {
                    await foreach (var item in batch.ResponseStream.ReadAllAsync())
                    {
                        Console.WriteLine($"服務端相應數據: {item.Message}");
                    }
                });

                await batchTask;
            }
        }
    }
}

客戶端流式處理方法

custom.proto

syntax = "proto3";

option csharp_namespace = "Custom.Service";

package custom;

service CustomGreeter {
  rpc SelfIncreaseClient(stream BathTheCatReq) returns (IntArrayModel); //客戶端流
}

message BathTheCatResp{
    string message = 1;
}

message IntArrayModel{
    repeated int32 number = 1;
}

CustomGreeterService.cs

using Grpc.Core;

namespace Custom.Service
{
    public class CustomGreeterService : CustomGreeter.CustomGreeterBase
    {
        public override async Task<IntArrayModel> SelfIncreaseClient(IAsyncStreamReader<BathTheCatReq> requestStream, ServerCallContext context)
        {
            IntArrayModel result = new IntArrayModel();
            while (await requestStream.MoveNext())
            {
                var message = requestStream.Current;  
                Console.WriteLine($"客戶端流傳入消息: {message}");
                result.Number.Add(message.Id + 1);
            }
            return result;
        }
    }
}

gRpcRequest.cs

using Custom.Service;
using Grpc.Core;
using Grpc.Net.Client;
using GrpcService;
using static Custom.Service.CustomGreeter;

namespace gRpcConsoleTest
{
    public static class gRpcRequest
    {
        public static async Task SelfIncreaseClient()
        {
            using (var channel = GrpcChannel.ForAddress("https://localhost:7166"))
            {
                CustomGreeterClient client = new CustomGreeterClient(channel);
                var batch = client.SelfIncreaseClient();
                for (int i = 0; i < 10; i++)
                {
                    await batch.RequestStream.WriteAsync(new BathTheCatReq() { Id = i });
                    await Task.Delay(1000);
                }
                await batch.RequestStream.CompleteAsync();

                foreach (var item in batch.ResponseAsync.Result.Number)
                {
                    Console.WriteLine($"響應數據: {item}");
                }
            }
        }
    }
}

雙向流式處理方法

custom.proto

syntax = "proto3";

option csharp_namespace = "Custom.Service";

package custom;

service CustomGreeter {
  rpc SelfIncreaseDouble(stream BathTheCatReq) returns (stream BathTheCatResp);//雙端流
}

message BathTheCatReq{
    int32 id = 1;
}

message BathTheCatResp{
    string message = 1;
}

CustomGreeterService.cs

using Grpc.Core;

namespace Custom.Service
{
    public class CustomGreeterService : CustomGreeter.CustomGreeterBase
    {
        public override async Task SelfIncreaseDouble(IAsyncStreamReader<BathTheCatReq> requestStream, IServerStreamWriter<BathTheCatResp> responseStream, ServerCallContext context)
        {
            while (await requestStream.MoveNext())
            {
                var message = requestStream.Current.Id;
                Console.WriteLine($"客戶端流傳入消息: {message}");
                await responseStream.WriteAsync(new BathTheCatResp() { Message=(message+1).ToString()});
            }
        }
    }
}

gRpcRequest.cs

using Custom.Service;
using Grpc.Core;
using Grpc.Net.Client;
using GrpcService;
using static Custom.Service.CustomGreeter;
using static GrpcService.Greeter;

namespace gRpcConsoleTest
{
    public static class gRpcRequest
    {
        public static async Task SelfIncreaseDouble()
        {
            using (var channel = GrpcChannel.ForAddress("https://localhost:7166"))
            {
                CustomGreeterClient client = new CustomGreeterClient(channel);
                var batch = client.SelfIncreaseDouble();

                Task batchTask = Task.Run(async () =>
                {
                    await foreach (var item in batch.ResponseStream.ReadAllAsync())
                    {
                        Console.WriteLine($"服務端相應數據: {item.Message}");
                    }
                });


                for (int i = 0; i < 10; i++)
                {
                    await batch.RequestStream.WriteAsync(new BathTheCatReq() { Id = i });
                    await Task.Delay(1000);
                }

                await batchTask;
            }
        }
    }
}

.Net Core 調用gRpc

項目引用

<PackageReference Include="Google.Protobuf" Version="3.24.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.55.0" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.55.0" />
<PackageReference Include="Grpc.Tools" Version="2.56.2">

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

Program.cs

//CustomGreeterClient grpc連接類
builder.Services.AddGrpcClient<CustomGreeterClient>(options =>
{
    options.Address = new Uri("https://localhost:7166"); //grpc 服務地址
});

gRpcController.cs

using Custom.Service;
using Microsoft.AspNetCore.Mvc;
using static Custom.Service.CustomGreeter;

namespace gRpcWebAPI.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class gRpcController : ControllerBase
    {
        CustomGreeterClient _client; //使用構造函數註入
        public gRpcController(CustomGreeterClient client)
        {
            _client = client;
        }

        [HttpGet]
        public async Task<IActionResult> Plus(int leftNumber, int rightNumber)
        {
            NumberResult number = await _client.PlusAsync(new Custom.Service.Number() { LeftNumber = leftNumber, RightNumber = rightNumber });
            return new JsonResult(number);
        }
    }
}

支持Aop

  1. 服務端, 客戶端 都需要繼承 Interceptor
  2. 重新需要實現Aop的方法,如服務端: UnaryServerHandler,客戶端: AsyncUnaryCall 針對一元調用的Aop
  3. 我這裡使用的是NLog寫的日誌,NLog可以使用可以翻閱我先前的博客
    .Net Core NLog+oracel

服務端 Program.cs

builder.Services.AddGrpc(options =>
{
    options.Interceptors.Add<LogInterceptor>();
});

服務端 LogInterceptor.cs

using Grpc.Core;
using Grpc.Core.Interceptors;

namespace GrpcService.Interceptors
{
    public class LogInterceptor : Interceptor
    {
        ILogger<LogInterceptor> _logger;
        public LogInterceptor(ILogger<LogInterceptor> logger)
        {
            _logger = logger;
        }
        public override Task<TResponse> UnaryServerHandler<TRequest, TResponse>(TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation)
        {
            _logger.LogInformation("===========UnaryServerHandler==========");
            return continuation(request, context);
        }
    }
}

客戶端 Program.cs

builder.Services.AddGrpcClient<CustomGreeterClient>(options =>
{
    options.Address = new Uri("https://localhost:7166"); //服務端地址
}).AddInterceptor<LogInterceptor>();

客戶端 LogInterceptor.cs

using Grpc.Core;
using Grpc.Core.Interceptors;
namespace GrpcService.Interceptors
{
    public class LogInterceptor : Interceptor
    {
        ILogger<LogInterceptor> _logger;
        public LogInterceptor(ILogger<LogInterceptor> logger)
        {
            _logger = logger;
        }
        public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
        {
            _logger.LogInformation("===========AsyncUnaryCall===========");
            return continuation(request, context);
        }
    }
}

jwt+gRPC驗證

  1. 準備單獨的一個網站發佈jwt Token (授權中心)
  2. 然後在gRPC項目中jwt鑒權 和 獲取到角色信息之後授權
  3. webapi測試gRPC調用, 先在授權中心獲取token然後在 ConfigureChannel 方法中設置gRPC全局的jwt token
  4. 返回401: jwt鑒權不通過
  5. 返回403: jwt授權不通過

準備Jwt Token發佈中心

Program.cs

//讀取Jwt配置
builder.Services.Configure<JwtConfig>(builder.Configuration.GetSection("JwtTokenOptions"));

JwtConfig.cs

namespace AuthenorizationCenter.Tools.Model
{
    public class JwtConfig
    {
        public string? Audience { get; set; }
        public string? Issuer { get; set; }
        public string? SecurityKey { get; set; }
        public int ExpiresMinutes { get; set; }
    }
}

AuthenorizationController.cs

using AuthenorizationCenter.Tools;
using AuthenorizationCenter.Tools.Model;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;

namespace AuthenorizationCenter.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthenorizationController : ControllerBase
    {

        JwtConfig _jwtconfig;
        public AuthenorizationController(IOptions<JwtConfig> jwtconfig) 
        {
            _jwtconfig = jwtconfig.Value;
        }

        [HttpGet]
        public async Task<string> GetToken(string userName, string passWord)
        {
            string token = JwtHeleper.GetToken(new()
            {
                UserName = userName,
                Extended1 = "無信息",
                Role = new List<RoleInfo>()
                {
                    new RoleInfo() { Id = "1",Role="系統管理員"} ,
                    new RoleInfo() { Id = "2",Role="用戶管理員"} ,
                }
            }, new()
            {
                Audience = _jwtconfig.Audience,
                Issuer = _jwtconfig.Issuer,
                SecurityKey = _jwtconfig.SecurityKey,
                ExpiresMinutes = 5,
            });

            await Task.CompletedTask;
            return token;
        }
    }
}

JwtHeleper.cs

using AuthenorizationCenter.Tools.Model;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace AuthenorizationCenter.Tools
{
    public class JwtHeleper
    {
        public static string GetToken(UserInfo user, JwtConfig jwtConfig)
        {
            List<Claim> claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, user.UserName ?? ""),
                new Claim("Extended1", user.Extended1 ?? ""),
                new Claim("Extended2", user.Extended2 ?? ""),
                new Claim("Extended3", user.Extended3 ?? ""),
                new Claim("Extended4", user.Extended4 ?? ""),
                new Claim("Extended5", user.Extended5 ?? ""),
            };
            if (user.Role is not null)
            {
                foreach (var item in user.Role)
                {
                    claims.Add(new Claim(item.Id.ToString(), item.Role));
                }
            }
            if (jwtConfig.SecurityKey == null)
            {
                throw new Exception("JwtConfig.SecurityKey 不能為空");
            }
            SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecurityKey));
            SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            JwtSecurityToken token = new JwtSecurityToken(
                issuer: jwtConfig.Issuer,
                audience: jwtConfig.Audience,
                claims: claims,
                expires: DateTime.UtcNow.AddMinutes(jwtConfig.ExpiresMinutes),
                signingCredentials: creds
            );
            string resultToken = new JwtSecurityTokenHandler().WriteToken(token);
            return resultToken;
        }
    }
}

RoleInfo.cs

namespace AuthenorizationCenter.Tools.Model
{
    public class RoleInfo
    {
        public string Id { get; set; }
        public string Role { get; set; }
    }
}

UserInfo.cs

namespace AuthenorizationCenter.Tools.Model
{
    public class UserInfo
    {
        public string? UserName { get; set; }
        public List<RoleInfo>? Role { get; set; }
        public string? Extended1 { get; set; }
        public string? Extended2 { get; set; }
        public string? Extended3 { get; set; }
        public string? Extended4 { get; set; }
        public string? Extended5 { get; set; }
    }
}

appsetting.json

{
  "JwtTokenOptions": {
    "Issuer": "https://localhost:7117",
    "Audience": "https://localhost:7117",
    "SecurityKey": "kq4DY5N1eFJhscOkI7Zp4Nd0WNy9d9AEsN6Yjgdv9OxLyol66tzGBKT_7vwolN7GZ8EDwqJBwccjDJfb81ws5s3sbbP5wUzQ3-PcTSsD-Rueiu2rsOUZwg_NR3RBCwmtouV-832YV2trCjNTawLB1z0LMukWGFNaAJVZ8WdQcrYn6a0ko5oVhZqaHBgsCLEGiqPtoFsiCcrJTz1IvXHk9_cDSr2hwEmSl18GlkOtgCHFH8aidYth3aQHRHuClTi6Y9mYRJtqqK-FNQYq4ZP23DSGZGFejJFTnM9YMpppuTMLklhSGySwX8rfjZ_0L5ac18nHaykTaiC2fvH00W42qQ"
  }
}

gRPC準備

Program.cs

JwtConfig jwtConfig = new JwtConfig();
builder.Configuration.Bind("JwtTokenOptions", jwtConfig);

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateIssuer = true,
        ValidIssuer = jwtConfig.Issuer, //發行人
        ValidateAudience = true,
        ValidAudience = jwtConfig.Audience,//訂閱人
        ValidateIssuerSigningKey = true,
        //對稱加密密鑰
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecurityKey!)),
        ValidateLifetime = true, //驗證失效時間
        ClockSkew = TimeSpan.FromSeconds(30), //過期時間容錯值
        RequireExpirationTime = true,
        AudienceValidator = (audiences, securityToken, validationParameters) =>
        {
            return true;
        },
        LifetimeValidator = (notBefore, expires, securityToken, validationParameters) =>
        {
            return true;
        }
    };
});

builder.Services.AddTransient<IUserServices, UserServices>();
builder.Services.AddTransient<IAuthorizationHandler, JwtAuthorization>();
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("JwtPolicy", policy =>
    {
        //jwt 授權
        policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
        //這裡為自定義授權指定一下類
        .AddRequirements(new UserRoleRequirement(JwtBearerDefaults.AuthenticationScheme));
    });
});

CustomGreeterService.cs

using Grpc.Core;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;

namespace Custom.Service
{
    public class CustomGreeterService : CustomGreeter.CustomGreeterBase
    {
        [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme,Policy = "JwtPolicy")]
        public override async Task<NumberResult> Plus(Number request, ServerCallContext context) => 
            await Task.FromResult<NumberResult>(new NumberResult()
            {
                Result = request.LeftNumber + request.RightNumber,
            });
    }
}

JwtAuthorization.cs

using Cnpc.Com.Ioc.IBll;
using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;

namespace GrpcService.Authorization
{
    public class UserRoleRequirement : IAuthorizationRequirement
    {
        public string AuthenticateScheme;
        public UserRoleRequirement(string authenticateScheme)
        {
            AuthenticateScheme = authenticateScheme;
        }
    }
    public class JwtAuthorization : AuthorizationHandler<UserRoleRequirement>
    {
        IUserServices userSercices;
        public JwtAuthorization(IUserServices userSercices)
        {
            this.userSercices = userSercices;
        }
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserRoleRequirement requirement)
        {
            string? userName = context.User.FindFirst(it => it.Type == ClaimTypes.Name)?.Value;
            if (userSercices.IsAdmin(userName!))
            {
                context.Succeed(requirement);
            }
            else
            {
                context.Fail();
            }
            return Task.CompletedTask;
        }
    }
}

客戶端調用

Program.cs

builder.Services.AddGrpcClient<CustomGreeterClient>(options =>
{
    options.Address = new Uri("https://localhost:7166");
}).AddInterceptor<LogInterceptor>().ConfigureChannel(async config =>
{
    //所有調用自動添加 Authorization
    CallCredentials credentials = CallCredentials.FromInterceptor(async (context, metadata) =>
    {
        string token = await HttpClientHelper.HttpGetAsync("https://localhost:7117/api/Authenorization?userName=admin&passWord=666");
        metadata.Add("Authorization", $"Bearer {token}");
    });
    config.Credentials = ChannelCredentials.Create(new SslCredentials(), credentials);
});

HttpClientHelper.cs

using Newtonsoft.Json;
using System.Text;

namespace gRpcWebAPI.Utility
{
    public static class HttpClientHelper
    {
        public static async Task<string> HttpGetAsync(string url, string contentType = "application/json", Dictionary<string, string> headers = null)
        {
            using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient())
            {
                if (contentType != null)
                    client.DefaultRequestHeaders.Add("ContentType", contentType);
                if (headers != null)
                {
                    foreach (var header in headers)
                        client.DefaultRequestHeaders.Add(header.Key, header.Value);
                }
                HttpResponseMessage response = await client.GetAsync(url);
                return await response.Content.ReadAsStringAsync();
            }
        }
    }
}

gRpcController.cs

using Custom.Service;
using Grpc.Core;
using gRpcWebAPI.Utility;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System.Security.Cryptography.X509Certificates;
using static Custom.Service.CustomGreeter;

namespace gRpcWebAPI.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class gRpcController : ControllerBase
    {
        CustomGreeterClient _client;
        public gRpcController(CustomGreeterClient client)
        {
            _client = client;
        }

        [HttpGet]
        public async Task<IActionResult> AsyncPlus(int leftNumber, int rightNumber)
        {
            try
            {
                //string token = await HttpClientHelper.HttpGetAsync("https://localhost:7117/api/Authenorization?userName=admin&passWord=666");
                //Metadata jwtCode = new Metadata { { "Authorization", $"Bearer {token}" } };
                NumberResult number = await _client.PlusAsync(new Custom.Service.Number() { LeftNumber = leftNumber, RightNumber = rightNumber });
                return new JsonResult(number);
            }
            catch (Exception ex)
            {
                return new JsonResult(ex.Message);
            }
        }

        [HttpGet]
        public IActionResult Plus(int leftNumber, int rightNumber)
        {
            try
            {
                string token = HttpClientHelper.HttpGet("https://localhost:7117/api/Authenorization?userName=admin&passWord=666");

                Metadata jwtCode = new Metadata { { "Authorization",$"Bearer {token}"} };
                NumberResult number = _client.Plus(new Custom.Service.Number() { LeftNumber = leftNumber, RightNumber = rightNumber },headers: jwtCode);
                return new JsonResult(number);
            }
            catch (Exception ex)
            {
                return new JsonResult(ex.Message);
            }
        }
    }
}

Rpc 與 Restful 區別

  • RPC是以一種調用本地方法的思路來調用遠程方法,通過各種RPC框架隱藏調用遠程方法的細節,讓用戶以為調用的就是本地方法。RPC隱藏了底層網路通信的複雜度,讓我們更專註於業務邏輯的開發。

  • REST通過HTTP實現,把用戶的需求抽象成對資源的操作,用戶必須通過HTTP協議的GET、HEAD、POST、PUT、DELETE、TRACE、OPTIONS七種基本操作去和伺服器交互。

  • RPC通常是伺服器和伺服器之間的通信,比如和中間件的通信,MQ、分散式緩存、分散式資料庫等等。

  • 而REST通常是面向客戶端的(一般是瀏覽器),他們的使用場景也是不一樣的。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • python 是一種高級、面向對象、通用的編程語言,由`Guido van Rossum`發明,於1991年首次發佈。python 的設計哲學強調代碼的可讀性和簡潔性,同時也非常適合於大型項目的開發。python 語言被廣泛用於Web開發、科學計算、人工智慧、自動化測試、游戲開發等各個領域,並且擁有... ...
  • Hibernate 是一個開源的 ORM(對象關係映射)框架,它可以將 Java 對象與資料庫表進行映射,從而實現面向對象的數據持久化。使用 Hibernate,可以避免手動編寫 SQL 語句,從而提高開發效率,並且可以輕鬆地切換不同的資料庫。 ## 基礎概念 entity 實體類是映射到資料庫表中 ...
  • 用PHP封裝一個強大且通用的cURL方法。 用PHP封裝一個強大且通用的cURL方法。 用PHP封裝一個強大且通用的cURL方法。 用PHP封裝一個強大且通用的cURL方法。 ```php /** * @function 強大且通用的cURL請求庫 * @param $url string 路徑 如 ...
  • AbstractRoutingDataSource是Spring框架中的一個抽象類,可以實現多數據源的動態切換和路由,以滿足複雜的業務需求和提高系統的性能、可擴展性、靈活性。 ...
  • 源碼請到:自然語言處理練習: 學習自然語言處理時候寫的一些代碼 (gitee.com) 數據來源:norvig.com/big.txt 貝葉斯原理可看這裡:機器學習演算法學習筆記 - 過客匆匆,沉沉浮浮 - 博客園 (cnblogs.com) 一、數據預處理 將輸入的數據全部變為小寫方便後續處理 de ...
  • 隨著互聯網的發展項目中的業務功能越來越複雜,有一些基礎服務我們不可避免的會去調用一些第三方的介面或者公司內其他項目中提供的服務,但是遠程服務的健壯性和網路穩定性都是不可控因素。 在測試階段可能沒有什麼異常情況,但上線後可能會出現調用的介面因為內部錯誤或者網路波動而出錯或返回系統異常,因此我們必須考慮 ...
  • 在mybatis中,我們在insert操作之後,可以獲取到自增主鍵的值,這個需要我們用到 INSERT INTO lawyer_id_offset (kgid) VALUES (#{kgid}) ``` # java中調用它 > 註意,這裡有個坑,獲取自增主鍵,不是獲取mapper的返回值,而是從當 ...
  • 字元串轉數字的用途和場景很多,其中主要包括以下幾個方面: 1. 數據清洗:在進行數據處理時,經常會遇到一些數據類型不匹配的問題,比如某些列中的字元串類型被誤認為是數字類型,此時需要將這些字元串類型轉換為數字類型,才能進行後續的數值計算或統計分析。 2. 數據整理:有時候輸入的原始數據可能存在格式問題 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...