一、前言
現實生產中,有一些比較老的系統對外提供的接口都是WebService,尤其是比較老的系統都是圍繞ESB進行搭建,而對外提供就需要WebService ,爲了更好完善其解決方案,故集成了webservice 協議組件和身份驗證,現把它上傳至github, 而這篇文章就是講述如何構建WebService,創建的接口IWebServiceService代碼如下:
using Surging.Core.CPlatform.Ioc; using Surging.Core.CPlatform.Runtime.Server.Implementation.ServiceDiscovery.Attributes; using Surging.IModuleServices.Common.Models; using System.ServiceModel; using System.Threading.Tasks; namespace Surging.IModuleServices.Common { [ServiceBundle("api/{Service}/{Method}")] [ServiceContract] public interface IWebServiceService : IServiceKey { [OperationContract] Task<string> SayHello(string name); [OperationContract] Task<string> Authentication(AuthenticationRequestData requestData); } }
AuthenticationRequestData 代碼如下:
using ProtoBuf; using System; using System.Collections.Generic; using System.Runtime.Serialization; using System.Text; namespace Surging.IModuleServices.Common.Models { [ProtoContract] [DataContract] public class AuthenticationRequestData { [ProtoMember(1)] [DataMember] public string UserName { get; set; } [ProtoMember(2)] [DataMember] public string Password { get; set; } } }
從以上代碼來看,除了需要符合引擎代碼規則外,還需要添加[ServiceContract]和[OperationContract] 特性, 如果參數是實體的話,需要添加在實體模型上加[DataContract]和 屬性上加[DataMember]
那麼創建的業務服務WebServiceService代碼如下:
using Surging.Core.ApiGateWay.OAuth; using Surging.Core.Protocol.WebService.Runtime; using Surging.IModuleServices.Common; using Surging.IModuleServices.Common.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Surging.Modules.Common.Domain { public class WebServiceService : WebServiceBehavior, IWebServiceService { private readonly IAuthorizationServerProvider _authorizationServerProvider; public WebServiceService(IAuthorizationServerProvider authorizationServerProvider) { _authorizationServerProvider = authorizationServerProvider; } public async Task<string> SayHello(string name) { var token = this.HeaderValue.Token; if (await _authorizationServerProvider.ValidateClientAuthentication(token)) return $"Hello,{name}"; else return " Please leave, stranger"; } public async Task<string> Authentication(AuthenticationRequestData requestData) { var param = new Dictionary<string, object>(); param.Add("requestData", requestData); var result= await _authorizationServerProvider.GenerateTokenCredential(param); return result; } } }
通過以上代碼,首先需要繼承IWebServiceService和WebServiceBehavior,然後通過IAuthorizationServerProvider 去生成Token 和驗證Token, 或者也可以脫離引擎的身份鑑權,通過傳遞的this.HeaderValue.Token 進行驗證。
通過訪問127.0.0.1:289/api/webservice/sayhello.asmx,顯示以下界面,說明基於webservice 的服務就已經添加成功。
二、引用WebService
首先我們在創建好的控制檯項目裏面添加WebService的引用。
1、在依賴項上面右鍵,選擇“添加服務引用”,選擇wcf web service如圖所示:
添加服務引用。如圖所示:
配置完以後,點擊“下一步”,去掉重新使用引用的程序集中的類型簽名的複選框。
直接點擊“完成”按鈕即可。慢慢等待配置完成:
配置完成界面如圖所示:
下面就介紹如何在.net 6.0下調用webservice
三、調用WebService
在Program類文件中,調用webservice 提供的sayhello,在調用前需要生成token, 通過token才能正確訪問結果,以下是基於.net 6.0,代碼如下:
1 // See https://aka.ms/new-console-template for more information 2 using ConsoleApp7; 3 using ServiceReference1; 4 using System.ServiceModel; 5 6 try 7 { 8 9 10 WebServiceServiceClient client = new WebServiceServiceClient(); 11 using (var scope = new FlowingOperationContextScope(client.InnerChannel)) 12 { 13 var authenticationResponse = await client.AuthenticationAsync(new AuthenticationRequestData 14 { 15 UserName = "admin", 16 Password = "admin" 17 }).ContinueOnScope(scope); 18 var authenticationResult = authenticationResponse.Body.AuthenticationResult; 19 if (authenticationResponse.Body.AuthenticationResult != null) 20 { 21 var header1 = System.ServiceModel.Channels.MessageHeader.CreateHeader("headerValue", "http://tempuri.org/", new HeaderValue 22 { 23 Token = authenticationResult 24 }); 25 OperationContext.Current.OutgoingMessageHeaders.Add(header1); 26 var sayHelloResponse =await client.SayHelloAsync("fanly").ContinueOnScope(scope); 27 Console.WriteLine(sayHelloResponse.Body.SayHelloResult); 28 Console.ReadLine(); 29 } 30 31 } 32 33 } 34 catch (Exception ex) 35 { 36 Console.WriteLine(ex.Message); 37 }
以下是基於.net framework 調用webservice 的代碼:
1 internal class Program 2 { 3 static async Task Main(string[] args) 4 { 5 try 6 { 7 8 WebServiceServiceClient client = new WebServiceServiceClient(); 9 using (var scope = new OperationContextScope(client.InnerChannel)) 10 { 11 var authenticationResponse = client.Authentication(new AuthenticationRequestData 12 { 13 UserName = "admin", 14 Password = "admin" 15 }); 16 if (authenticationResponse != null) 17 { 18 var header1 = System.ServiceModel.Channels.MessageHeader.CreateHeader("headerValue", "http://tempuri.org/", new HeaderValue 19 { 20 Token = authenticationResponse 21 }); 22 OperationContext.Current.OutgoingMessageHeaders.Add(header1); 23 var sayHelloResponse = client.SayHello("fanly"); 24 Console.WriteLine(sayHelloResponse); 25 Console.ReadLine(); 26 } 27 28 } 29 30 } 31 catch (Exception ex) 32 { 33 Console.WriteLine(ex.Message); 34 } 35 } 36 }
HeaderValue 代碼如下:
namespace ConsoleApp7 { public class HeaderValue { public string Token { get; set; } } }
因爲.net 6.0 生成的代碼是異步,所以就要修改OperationContextScope 以支持異步,代碼如下:
1 public sealed class FlowingOperationContextScope : IDisposable 2 { 3 bool _inflight = false; 4 bool _disposed; 5 OperationContext _thisContext = null; 6 OperationContext _originalContext = null; 7 8 public FlowingOperationContextScope(IContextChannel channel): 9 this(new OperationContext(channel)) 10 { 11 } 12 13 public FlowingOperationContextScope(OperationContext context) 14 { 15 _originalContext = OperationContext.Current; 16 OperationContext.Current = _thisContext = context; 17 } 18 19 public void Dispose() 20 { 21 if (!_disposed) 22 { 23 if (_inflight || OperationContext.Current != _thisContext) 24 throw new InvalidOperationException(); 25 _disposed = true; 26 OperationContext.Current = _originalContext; 27 _thisContext = null; 28 _originalContext = null; 29 } 30 } 31 32 internal void BeforeAwait() 33 { 34 if (_inflight) 35 return; 36 _inflight = true; 37 } 38 39 internal void AfterAwait() 40 { 41 if (!_inflight) 42 throw new InvalidOperationException(); 43 _inflight = false; 44 OperationContext.Current = _thisContext; 45 } 46 } 47 48 public static class TaskExt 49 { 50 public static SimpleAwaiter<TResult> ContinueOnScope<TResult>(this Task<TResult> @this, FlowingOperationContextScope scope) 51 { 52 return new SimpleAwaiter<TResult>(@this, scope.BeforeAwait, scope.AfterAwait); 53 } 54 55 public class SimpleAwaiter<TResult> : 56 System.Runtime.CompilerServices.INotifyCompletion 57 { 58 readonly Task<TResult> _task; 59 60 readonly Action _beforeAwait; 61 readonly Action _afterAwait; 62 63 public SimpleAwaiter(Task<TResult> task, Action beforeAwait, Action afterAwait) 64 { 65 _task = task; 66 _beforeAwait = beforeAwait; 67 _afterAwait = afterAwait; 68 } 69 70 public SimpleAwaiter<TResult> GetAwaiter() 71 { 72 return this; 73 } 74 75 public bool IsCompleted 76 { 77 get 78 { 79 if (_task.IsCompleted) 80 return true; 81 _beforeAwait(); 82 return false; 83 } 84 85 } 86 87 public TResult GetResult() 88 { 89 return _task.Result; 90 } 91 92 // INotifyCompletion 93 public void OnCompleted(Action continuation) 94 { 95 _task.ContinueWith(task => 96 { 97 _afterAwait(); 98 continuation(); 99 }, 100 CancellationToken.None, 101 TaskContinuationOptions.ExecuteSynchronously, 102 SynchronizationContext.Current != null ? 103 TaskScheduler.FromCurrentSynchronizationContext() : 104 TaskScheduler.Current); 105 } 106 } 107 }
程序輸出結果:
四、結尾
surging 正在開發微服務平臺(以處於調試階段),形成獨立的項目產品,拋棄之前的代碼架構的形式,現如今已經攘括支持WEB, 物聯網,流媒體等多種業務場景, 現在開發支持了外層協議有:MQTT,Grpc,, DNS, TCP,UDP,restful,rtmp,httpflv,rtsp,websocket,webservice, 內部可以通過基於thrift 或者netty 做到可靠性的RPC調用,因爲有服務治理,服務發現,並且支持了Apollo配置中心,skywalking 鏈路跟蹤,並且支持JAVA和.NET主流開發語言,請大家多多留意surging 的微服務平臺。或者你也可以加羣聯繫到我:744677125(老羣被封,這是新羣)