开发可扩展的 Web API






4.80/5 (4投票s)
使用 ASP.NET Web API2 开发一个非常简单的 Web API 架构,其操作可以通过仅添加新的操作类型来扩展
↵
引言
我最近需要准备一个 Web API,它有几个小操作,并且会不断添加新的操作。 我决定使用可扩展的架构,而不是添加许多包含少量操作的控制器。 因此,只能通过添加新的操作类型来添加新操作。
架构
该架构和项目非常简单。 该项目是使用 Visual Studio 2017 的 Web API 2 项目模板创建的。 只有一个 API 控制器,名为 OperationsController
,其中只有一个名为 Process
的操作。 有一个简单的 IOperation
接口,一个操作必须实现该接口,以及一个名为 OperationBase
的基类,用于样板代码。
Autofac 和 Autofac WebAPI2 集成包用于自动发现操作类型并将它们注入到控制器中。
代码
IOperation
接口如下
public interface IOperation
{
string Name { get; }
string Description { get; }
Type ParameterClassType { get; }
object Execute(object prm);
}
ParameterClassType
是 Execute
方法需要的参数对象的类型。 它用于解析 request
主体中的 JSON 数据。 如果 Execute
方法返回一个非 null
值,它将作为结果返回给调用者,否则只返回 Ok
响应。
OperationController
及其 Process
操作如下
public class OperationsController : ApiController
{
//Implemented operations are injected in the constructor
public OperationsController(IEnumerable<IOperation> supportedOperations)
{
_supportedOperations = new List<IOperation>(supportedOperations);
}
//Single action that gets the operation name and
//reads the operation parameters as JSON from request body
[HttpPost]
public async Task<IHttpActionResult> Process(string operationName)
{
//Find the operation
IOperation operation = _supportedOperations.FirstOrDefault(x => x.Name == operationName);
if (operation == null)
return BadRequest($"'{operationName}' is not supported.");
//Get the request body as string
string jsonBody = await Request.Content.ReadAsStringAsync();
object operationParams = null;
try
{
//Parse the JSON data in the request body t construct operation's parameter object
if (operation.ParameterClassType != null)
operationParams = Newtonsoft.Json.JsonConvert.DeserializeObject
(jsonBody, operation.ParameterClassType);
object result = operation.Execute(operationParams);
//Return the result value as JSON to the caller
if (result != null)
return Json(result);
}
catch(Exception ex)
{
return InternalServerError(ex);
}
//Return Ok if the operation has no return value
return Ok();
}
}
只需要 operationName
值的路由配置如下
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{operationName}",
defaults: new { controller = "operations", action = "process", operationName = "help" }
);
此路由配置将所有请求路由到 OperationsControlller
的 Process
操作。 如果没有提供操作名称,则执行帮助。
Autofac 依赖注入已配置并设置为 Web API 管道的依赖关系解析器
ContainerBuilder builder = new ContainerBuilder();
//Register controllers
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
//Register all operations
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.Where(t=> t.IsAssignableTo<IOperation>())
.As<IOperation>();
var container = builder.Build();
//Set Autofac container as the dependency resolver
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
使用 Postman 进行测试
测试无参数操作“help”。
从“help”操作返回的响应。
测试“sum”操作,该操作需要一个具有属性“Number1
”和“Number2
”的对象。
从“sum”操作返回的响应。
后续步骤
为了仅演示这个想法,代码保留得非常简单。 将命令处理操作与控制器类分开是件好事。
帮助命令仅显示操作的名称和描述,但最好提供特定操作是否需要参数以及它们的名称类型。 因为操作不是控制器操作,所以 Swagger 不会帮助提供参数信息。
也没有自动验证。 可以在操作的 Execute
方法之前完成验证,或者每个操作都可以执行自己的验证逻辑。
还应该添加身份验证和授权操作以保护 Web API。
历史
- 2019 年 7 月 3 日:初始版本