WebApi实现原理

什么是 WebApi

WebApi 就是网络接口,运行流程如下。image

如何实现

1 最开始的执行方法

如果要执行一个方法,最简单的会通过如下几步执行。

[ApiController]
[Router("WeatherForecast")]
public class WeatherForecastController
{
    [Router("/Get")]
    public Random Get()
    {
        var rng = new Random();
        return rng;
    }

    public Random Post()
    {
        var rng = new Random();

        return rng;
    }

    public Random Put()
    {
        var rng = new Random();

        return rng;
    }

    public Random Delete()
    {
        var rng = new Random();

        return rng;
    }
}
// 1、获取反射类型
Type type = typeof(WeatherForecastController);

// 2、创建对象
object _obejct = Activator.CreateInstance(type);

// 3、获取方法
MethodInfo methodInfo = type.GetMethod("Get");
//MethodInfo methodInfo = type.GetMethod("Post");

// 4、执行方法
object result = methodInfo.Invoke(_obejct, new object[] { });

2 第一次改造,加入建造者模式加载所有方法到集合

但是上面代码只能是给定类,是写死的,我们需要动态把所有 Controller 中方法都获取到。

添加 Endpoint 类,里面存储方法的信息。

/// <summary>
/// 端点(就是Action的抽象)
/// </summary>
public class Endpoint
{
    /// <summary>
    /// 方法信息
    /// </summary>
    public MethodInfo MethodInfo { set; get; }

    /// <summary>
    /// 控制器
    /// </summary>
    public object Controller { set; get; }

    /// <summary>
    /// 方法参数类型
    /// </summary>
    public IList<ParameterInfo> ParameterInfos { set; get; }

    /// <summary>
    /// 方法返回值
    /// </summary>
    public object ReturnValue { set; get; }

    // 数据输出格式(json,xml)
    public string JataType { set; get; }

}

添加 Endpoint 数据源,用于存储 Endpoint 数据集合。

/// <summary>
/// Endpoint数据源(存储Endpoint数据集合)
/// </summary>
class EndpointDataSources
{
    /// <summary>
    /// Endpoint字典 key路径value端点信息
    /// </summary>
    private Dictionary<string, Endpoint> EndpointDic = new Dictionary<string, Endpoint>();

    public void AddEndpoint(string path, Endpoint endpoint)
    {
        EndpointDic.Add(path, endpoint);
    }
    /// <summary>
    /// 提供Endpoint
    /// </summary>
    /// <param name="endpointName"></param>
    /// <returns></returns>
    public Endpoint this[string MethodName]
    {
        get { return EndpointDic[MethodName]; }
    }
}

EndpointDataSourcesBuilder 用于构建 EndpointDataSources,其中 MapControllers 方法就是循环获取到所有 Controller 中的方法加入字典。

/// <summary>
/// 构建EndpointDataSources(建造者设计模式)
/// </summary>
class EndpointDataSourcesBuilder
{
    /// <summary>
    /// Endpoint集合
    /// </summary>
    private EndpointDataSources endpointDataSources = new EndpointDataSources();

    public void MapControllers()
    {
        Assembly assembly = Assembly.Load("WebApiCode");
        Type[] types = assembly.GetTypes();
        foreach (var type in types)
        {
            // 1、实现控制器过滤
            ApiController apiController = type.GetCustomAttribute<ApiController>();
            if (apiController != null)
            {
                // 1、获取控制器路径
                Router controllerRouter = type.GetCustomAttribute<Router>();

                // 2、创建对象
                object _obejct = Activator.CreateInstance(type);

                // 3、获取方法
                MethodInfo[] methodInfos = type.GetMethods();
                foreach (var methodInfo in methodInfos)
                {
                    // 特性过滤,路径映射
                    Router router = methodInfo.GetCustomAttribute<Router>();
                    if (router != null)
                    {
                        // 端点映射
                        Endpoint endpoint = new Endpoint
                        {
                            MethodInfo = methodInfo,
                            Controller = _obejct
                        };

                        // 获取控制器方法路径
                        string path = controllerRouter.Path + router.Path;
                        endpointDataSources.AddEndpoint(path, endpoint);
                    }
                }
            }
        }
    }

    public EndpointDataSources Build()
    {
        return endpointDataSources;
    }
}

相关特性。

/// <summary>
///  API控制器
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
class ApiController : Attribute 
{ }

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
class Router : Attribute
{
    /// <summary>
    /// 路径
    /// </summary>
    public string Path { get; }

    public Router(string path)
    {
        Path = path;
    }
}

经过以上改造,客户端就可以这样写了。

//1、创建EndpointDataSourcesBuilder对象并构建EndpointDataSources
EndpointDataSourcesBuilder builder = new();
builder.MapControllers();
EndpointDataSources endpointData = builder.Build();
//2、获取Endpoint
var endpoint = endpointData["WeatherForecast/Get"];
//3、获取方法
MethodInfo methodInfo = endpoint.MethodInfo;
//4、执行方法
methodInfo.Invoke(endpoint.Controller, new object[] { });

3 第二次改造,加入路由匹配

因为 WebApi 请求是通过路由找到执行的方法,所以我们需要加入路由。

添加模拟 Http 相关类。

/// <summary>
/// http上下文类(用于获取HttpRequest和HttpResponse)
/// </summary>
public class HttpContext
{
    public string RequestPath { set; get; }

    public HttpRequest Request { set; get; }
    public HttpResponse Response { set; get; }
}
public class HttpRequest { }
public class HttpResponse { }

添加路由路径匹配。

/// <summary>
/// Endpoint 路由:通过路径获取EndPoint
/// </summary>
class EndpointRoute
{
    public EndpointDataSources endpointDataSources { get; set; }

    /// <summary>
    /// 路由匹配
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public Endpoint Match(HttpContext httpContext)
    {
        // 路径判断
        return endpointDataSources[httpContext.RequestPath];
    }
}

客户端变成如下。

//1、创建EndpointDataSourcesBuilder对象并构建EndpointDataSources
EndpointDataSourcesBuilder builder = new();
builder.MapControllers();
EndpointDataSources endpointData = builder.Build();

//2、获取Endpoint
HttpContext httpContext = new()
{
    RequestPath = "WeatherForecast/Get"
};
EndpointRoute route = new()
{
    endpointDataSources = endpointData
};
var endpoint = route.Match(httpContext);

//3、获取方法
MethodInfo methodInfo = endpoint.MethodInfo;
//4、执行方法
methodInfo.Invoke(endpoint.Controller, new object[] { });

4 第三次改造,端点执行进行封装

添加端点处理类 EndpointHandler。

/// <summary>
/// 端点处理
/// </summary>
public class EndpointHandler
{
    /// <summary>
    /// 执行端点
    /// </summary>
    /// <param name="endpoint"></param>
    /// <param name="providedArgs">方法参数值</param>
    /// <returns></returns>
    public object HandlerEndpoint(Endpoint endpoint, object[] providedArgs)
    {
        // 1、获取方法
        MethodInfo methodInfo = endpoint.MethodInfo;
        // 2、执行
        return methodInfo.Invoke(endpoint.Controller, providedArgs);
    }
}

客户端修改。

//1、创建EndpointDataSourcesBuilder对象并构建EndpointDataSources
EndpointDataSourcesBuilder builder = new();
builder.MapControllers();
EndpointDataSources endpointData = builder.Build();

//2、获取Endpoint
HttpContext httpContext = new()
{
    RequestPath = "WeatherForecast/Get"
};
EndpointRoute route = new()
{
    endpointDataSources = endpointData
};
var endpoint = route.Match(httpContext);

//3、执行Endpoint
EndpointHandler endpointHandler = new();
endpointHandler.HandlerEndpoint(endpoint, new object[] { });

5 第四次改造,使用外观模式封装成中间件

既然以上 WebApi 的执行流程已经确定,那么我们可以使用外观模式将其封装成中间件调用。

添加终结点中间件 EndpointMiddleware,把过程封装进 Invoke 方法。

/// <summary>
/// 终结点中间件(外观模式)。
/// </summary>
class EndpointMiddleware
{
    private EndpointDataSources endpointDataSources;
    private EndpointRoute endpointRoute;
    private EndpointHandler endpointHandler;

    public EndpointMiddleware()
    {
        EndpointDataSourcesBuilder endpointDataSourcesBuilder = new EndpointDataSourcesBuilder();
        endpointDataSourcesBuilder.MapControllers();
        this.endpointDataSources = endpointDataSourcesBuilder.Build();
        this.endpointRoute = new EndpointRoute();
        this.endpointHandler = new EndpointHandler();
    }

    /// <summary>
    /// 执行请求
    /// </summary>
    /// <param name="httpContext"></param>
    public void Invoke(HttpContext httpContext)
    {
        // 获取Endpoint
        endpointRoute.endpointDataSources = endpointDataSources;
        Endpoint endpoint = endpointRoute.Match(httpContext);

        // 执行endpoint
        object result = endpointHandler.HandlerEndpoint(endpoint, new object[] { });
    }
}

客户端直接用中间件调用。

HttpContext httpContext = new()
{
    RequestPath = "WeatherForecast/Get"
};
EndpointMiddleware endpointMiddleware = new();
endpointMiddleware.Invoke(httpContext);

6 最终所添加的类如下所示

image

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值