HTTP请求组件 BrokerClient
BrokerClient主要用于支持各类Http请求
如何引入
功能包含在Aegis.Net.Broker的Nuget包中,当前最新版本是1.0.7-preview1。
BrokerClient不需要使用任何配置
快速使用
使用BrokerClient前,首先需要定义调用Http服务的契约接口
比如这个例子,定义了HIS下调用医生站的契约接口
public interface IDoctorContract
{
[Post("/api/his-api/receiveHisUpdate")]
public Task<BaseDto<string>> ReceiveHisUpdateAsync([Body] QuerWardBedTotalRequest request);
[Post("/api/emr/get-patient-emr-pdf")]
public Task<BaseDto<List<GetPatientEmrPdfDto>>> GetPatientEmrPdfAsync([Body] GetPatientEmrPdfRequest request);
}
定义好接口好,在具体需要调用的类中直接使用BrokerClient.Get就可以直接获取对应接口,在调用的时候直接使用契约接口调用方法即可。
public class OpDoctorService : IBusinessService
{
IDoctorContract _doctorContract;
public OpDoctorService(ILogger<OpDoctorService> _logger, IApiLogsService apiLogsService)
{
_doctorContract = BrokerClient.Get<IDoctorContract>(ConfigManager.Get("OpDoctorURL"));
}
public async Task CallUpdate(QuerWardBedTotalRequest request)
{
var result = await _doctorContract.ReceiveHisUpdateAsync(request);
...
}
}
请求相关
在定义具体契约接口时,可以使用各种Attribute来定义该接口的HTTP相关地址和方法信息。
路径
https://api.example.com/api/v1/users/getuser,这个路径可以拆分为三部分
- Base Address 基础地址,常用于域名,IP地址等。一般使用配置读取,在这里是
https://api.example.com - Base Path 基础路径,用于相对固定路由路径。这里是
api/v1/users - Method Path 方法路径,标注具体方法。这里是
getuser
Base Address 基础地址
设置接口的基础地址有两种方案
- 在接口最上方使用
[BaseAddress(...)],不推荐该方案,一般基础地址都是从配置或数据中读取的 - 使用
BrokerClient.Get<T>(...)或new BrokerClient(...),推荐使用这种方式,就像快速使用的例子,BrokerClient.Get<IDoctorContract>(ConfigManager.Get("OpDoctorURL"))一样。
Base Path 基础路径
可以设置在接口上标注某个领域或者固定的基础路径,如下面的例子
[BasePath("api/v1/users")]
public interface IUserContract
Method Path 方法路径
用于设置具体的方法路径
[BasePath("api/v1/users")]
public interface IUserContract
{
[Post("getuser")]
public Task<List<User>> GetUsers([Body]UserRequest request);
}
可以在接口上直接定义该接口的请求,支持各类HTTP方法,比如 [Get("path")]、 [Post("path")]等。
返回类型
目前契约接口只支持异步调用,所以所有的返回类型都带有Task
返回的数据类型,一共有以下几种类型:
Task无返回值结果Task<T>指定泛型返回值Task<string>返回string类型的结果消息Task<HttpResponseMessage>返回HttpResponseMessage,具体参考微软文档Task<Response<T>>返回包含返回HttpResponseMessage和指定泛型返回值的结果Task<Stream>返回包含流的结果,主要用于下载文件
指定泛型返回值的结果会默认使用Json序列化器返回。
[BasePath("api/v1/users")]
public interface IUserContract
{
//添加用户,不需要验证是否成功
[Post("adduser")]
public Task AddUser([Body]AddUserRequest request); //无返回值
//获取用户列表
[Post("getuser")]
public Task<List<User>> GetUsers([Body]UserRequest request); //指定泛型返回值
//获取用户说明的XML对象,使用字符串查询后续用XML序列器序列化
[Get("getuserdescription")]
public Task<string> GetUserDescription([Query]UserRequest request);
//更新用户信息,因为调了第三方接口可能会失败,所以获取HttpResponseMessage的原始内容来判断是否异常
[Post("changeuserstate")]
public Task<HttpResponseMessage> ChangeUserState([Body]UserRequest request);
//更新用户,可以用结果直接GetContent()来获取泛型结果,也可以用Response里的message来验证是否异常
[Post("updateuser")]
public Task<Response<User>> UpdateUser([Body]UserRequest request);
//下载文件
[Post("download")]
public Task<Stream> Download([Body]UserFilterRequest request);
}
Header
在日常请求中,经常需要带各种类型的Header,无论是固定的header,还是用于验证的header,Broker为header设置提供了较为便利的方式。
固定Header设置
定义在接口上
[Header("X-Source", "His")]
[Header("Cache-Control", "no-cache")]
public interface IUserContract
{
[Get("users")]
Task<List<User>> GetUsersAsync();
}
定义在方法上
[Header("Cache-Control", "no-cache")]
public interface IUserContract
{
[Header("X-Source", "His")]
[Get("users")]
Task<List<User>> GetUsersAsync();
}
可变Header设置
一般用于传Authorization和Token之类的可变Header头
定义在接口属性上,所有该接口下的方法请求出去时都会将该属性带在header上。
public interface IUserContract
{
[Header("Authorization")]
string Authorization { get; set; }
[Get("users")]
Task<List<User>> GetUsersAsync();
}
定义在方法参数上,在调用该方法时传入具体数据
public interface IUserContract
{
[Get("users")]
Task<List<User>> GetUsersAsync([Header("Authorization")] string authorization);
}
Header优先级
当接口Attribute、属性和方法参数上都包含同名Header时,并都设置了值。这时候的Header优先级是 方法参数 > 接口属性 > 接口Attribute,最终会使用方法参数的值请求出去。
[Header("Authorization","1")]
public interface IUserContract
{
[Header("Authorization")]
string Authorization { get; set; }
[Get("users")]
Task<List<User>> GetUsersAsync([Header("Authorization")] string authorization);
}
方法参数设置
支持所有类型的参数设置,参照下列代码:
public interface IUserContract
{
//GetUser?userId=xx&age=18
[Get("user")]
Task<List<User>> GetUserAsync([Query]string userId,[Query]string age);
//GetUsers,参数带在raw body中的json
[Post("getusers")]
Task<List<User>> GetUsers([Body]UserRequest request);
//添加用户的表单式提交,(也就是类型为application/x-www-form-urlencoded的请求)
[Post("adduser")]
Task<List<User>> AddUser([Body(BodySerializationMethod.UrlEncoded)]Dictionary<string,object> request);
//Url重写类型的请求,把参数定义在Path中
[Get("users/{userId}")]
Task<User> GetUserAsync([Path] string userId);
//上传文件,使用流作为Body即可
[Header("Content-Disposition", "form-data; filename=\"somefile.txt\"")] //这里填文件名
[Header("Content-Type", "text/plain")] //定义具体的媒体类型
[Post("upload")]
Task UploadFile([Body] Stream file);
}
需要注意的是
BodySerializationMethod.UrlEncoded的表单式提交只支持使用IDictionary或Dictionary提交数据。
Broker组件支持Webservice访问
如何引入
在项目中引入Broker支持Webservice访问的相关依赖。
功能包含在Aegis.Net.Broker的Nuget包中,当前最新版本是3.0.0-preview1
快速使用
使用BrokerClient前,首先需要定义调用Webservice服务的契约接口 契约接口
using System.ServiceModel;
namespace Aegis.Webapi.BaseDemo.Contracts.Soap
{
[ServiceContract]
public interface ISampleService
{
// 简单的回显方法,接受字符串并返回相同字符串
[OperationContract]
string Ping(string s);
}
}
定义好契约接口后,即可使用BrokerClient调用Webservice服务, 示例代码如下: 调用方法在契约接口中定义的方法。
using Aegis.Core.Infrastructure.Controller;
using Aegis.Net.Broker;
using Aegis.Transfer.Responses;
using Aegis.Webapi.BaseDemo.Contracts.Soap;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Aegis.Webapi.BaseDemo.Controllers
{
[AllowAnonymous]
public class BrokerSoapTestController : ApiControllerBase
{
ISampleService _sampleService;
public BrokerSoapTestController()
{
_sampleService = BrokerClient.Get<ISampleService>("http://localhost:5050/Service.svc");
}
[HttpGet("PingSoap")]
public string PingSoap()
{
string message = "Hello, SOAP Service!";
return _sampleService.Ping(message);
}
}
}
请求相关
支持asmx创建的Webservice服务端请求例子
简要介绍
ASMX: 已被 WCF 取代,属于较旧的技术 , 但仍然有许多遗留的系统在使用它。
ASMX(Active Server Method Extensions)是微软早期用于创建Web服务的技术,允许开发者通过简单的方式发布和消费Web服务。
通常创建的Webservice服务地址以.asmx结尾。
请求体定义
ComplexInput类定义 ,注解中命名空间为Webservice中配置的命名空间
using System.Runtime.Serialization;
using System.Xml.Serialization;
namespace Aegis.Webapi.BaseDemo.Contracts.Webservice
{
[DataContract(Namespace = "http://tempuri.org/")]
public class ComplexInput
{
[DataMember]
public int IntValue { get; set; }
[DataMember]
public string StringValue { get; set; }
[DataMember]
public DateTime DateTimeValue { get; set; }
[DataMember]
public InnerClass InnerObject { get; set; }
}
}
嵌套类InnerClass定义,注解中命名空间为Webservice中配置的命名空间
using System.Runtime.Serialization;
using System.Xml.Serialization;
namespace Aegis.Webapi.BaseDemo.Contracts.Webservice
{
[DataContract(Namespace = "http://tempuri.org/")]
public class InnerClass
{
[DataMember]
public string InnerValue { get; set; }
}
}
返回的定义
ComplexOutput,注解中命名空间为Webservice中配置的命名空间
using System.Runtime.Serialization;
using System.Xml.Serialization;
namespace Aegis.Webapi.BaseDemo.Contracts.Webservice
{
[DataContract(Namespace = "http://tempuri.org/")]
public class ComplexOutput
{
[DataMember]
public double DoubleValue { get; set; }
[DataMember]
public bool BooleanValue { get; set; }
[DataMember]
public string ResultMessage { get; set; }
[DataMember]
public InnerClass InnerObject { get; set; }
}
}
定义契约接口
其中 注解中的Action的值为Webservice中定义的 命名空间/方法名
MessageParameter 的值为 Webservice中定义的返回的类名
using System.ServiceModel;
namespace Aegis.Webapi.BaseDemo.Contracts.Webservice
{
[ServiceContract]
[XmlSerializerFormat]
public interface ISampleService
{
[OperationContract(Action = "http://tempuri.org/ProcessComplexType")]
[return: MessageParameter(Name = "ComplexOutput")]
public ComplexOutput ProcessComplexType([MessageParameter(Name = "ComplexInput")] ComplexInput input);
}
}
调用
public class BrokerWebserviceTest1Controller : ApiControllerBase
{
ISampleService _sampleService;
public BrokerWebserviceTest1Controller()
{
_sampleService = BrokerClient.Get<ISampleService>("http://localhost:58471/WebService1.asmx");
}
[HttpGet("ProcessComplexTypeWebservice")]
public async Task<ApiResponse> ProcessComplexTypeWebservice()
{
var inner = new InnerClass
{
InnerValue = "Inner Value from Client"
};
var input = new ComplexInput
{
InnerObject = inner,
IntValue = 42,
StringValue = "Test String from Client",
DateTimeValue = DateTime.Now
};
var result = _sampleService.ProcessComplexType(input);
return SuccessResult(result);
}
}
支持WCF创建的Webservice服务端请求例子
简要介绍
WCF: 是 Microsoft 推荐的通信框架
WCF(Windows Communication Foundation)是微软推出的一种用于构建服务导向应用程序的框架。
一般创建的Webservice服务地址以 .svc 结尾。
请求体定义
ComplexModelInput类定义
其中Namespace的值为WCF中定义的类的命名空间,可以通过sopUI或其他工具查看
using System.Runtime.Serialization;
using System.ServiceModel;
namespace Aegis.Webapi.BaseDemo.Contracts.Soap
{
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/Models")]
public class ComplexModelInput
{
[DataMember]
public string StringProperty { get; set; }
[DataMember]
public int IntProperty { get; set; }
[DataMember]
public List<string> ListProperty { get; set; }
[DataMember]
public DateTimeOffset DateTimeOffsetProperty { get; set; }
[DataMember]
public List<ComplexObject> ComplexListProperty { get; set; }
}
[DataContract]
public class ComplexObject
{
[DataMember]
public string StringProperty { get; set; }
[DataMember]
public int IntProperty { get; set; }
}
}
返回的定义
PingComplexModelResponse类
其中Namespace的值为WCF中定义的类的命名空间,可以通过sopUI或其他工具查看
using System.Runtime.Serialization;
namespace Aegis.Webapi.BaseDemo.Contracts.Soap
{
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/Models")]
public class ComplexModelResponse
{
[DataMember]
public float FloatProperty { get; set; }
[DataMember]
public string StringProperty { get; set; }
[DataMember]
public List<string> ListProperty { get; set; }
[DataMember]
public DateTimeOffset DateTimeOffsetProperty { get; set; }
[DataMember]
public TestEnum TestEnum { get; set; }
}
public enum TestEnum
{
One,
Two
}
}
定义契约接口
在契约接口中定义PingComplexModel方法
using System.ServiceModel;
namespace Aegis.Webapi.BaseDemo.Contracts.Soap
{
[ServiceContract]
public interface ISampleService
{
// 接收复杂输入模型并返回复杂响应
[OperationContract]
ComplexModelResponse PingComplexModel(ComplexModelInput inputModel);
}
}
调用
在Controller中调用PingComplexModel方法
ISampleService _sampleService;
public BrokerSoapTestController()
{
_sampleService = BrokerClient.Get<ISampleService>("http://localhost:5050/Service.svc");
}
[HttpGet("PingComplexModelSoap")]
public async Task<ApiResponse> PingComplexModelSoap()
{
ComplexModelInput complexModelInput = new ComplexModelInput
{
StringProperty = "TestString",
IntProperty = 42,
ListProperty = new List<string> { "Item1", "Item2", "Item3" },
DateTimeOffsetProperty = DateTimeOffset.UtcNow,
ComplexListProperty = new List<ComplexObject>
{
new ComplexObject { StringProperty = "Complex1", IntProperty = 100 },
new ComplexObject { StringProperty = "Complex2", IntProperty = 200 }
}
};
var response = _sampleService.PingComplexModel(complexModelInput);
return SuccessResult(response);
}
对soap1.2 的支持
以上示例均为soap1.1的请求方式,若需要使用soap1.2的请求方式 在定义契约接口时 添加如下注解
using System;
namespace Aegis.Net.Broker;
/// <summary>
/// 标记接口为 Webservice 接口契约。
/// </summary>
[AttributeUsage(AttributeTargets.Interface)]
public sealed class SoapContractAttribute : Attribute
{
/// <summary>
/// 获取或设置 SOAP 版本。soap11 或 soap12。
/// </summary>
public string SoapVersion { get; set; }
}
示例修改ISampleService:
[SoapContract(SoapVersion = "soap12")]
[ServiceContract]
[XmlSerializerFormat]
public interface ISampleService
{
[OperationContract]
ComplexModelResponse PingComplexModel(ComplexModelInput inputModel);
}