什么是 WebApi
WebApi 就是网络接口,运行流程如下。
如何实现
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);