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 基础地址

设置接口的基础地址有两种方案

  1. 在接口最上方使用[BaseAddress(...)],不推荐该方案,一般基础地址都是从配置或数据中读取的
  2. 使用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,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设置

一般用于传AuthorizationToken之类的可变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的表单式提交只支持使用IDictionaryDictionary提交数据。

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);
}

results matching ""

    No results matching ""