29、飞行荷兰人航空公司服务控制器层实现指南

飞行荷兰人航空公司服务控制器层实现指南

1. 引言

在之前的工作中,我们已经完成了数据库访问层、仓库层和服务层的开发。然而,目前的服务还无法被外部系统使用,因为缺少能够接收 HTTP 请求并进行相应处理的控制器。本文将详细介绍如何实现控制器层,特别是 FlightController ,并探讨相关的技术细节。

2. 控制器在 Repository/Service 模式中的位置

控制器通常是 Repository/Service 模式中面向公众的最顶层。在这个模式中,控制器是与外部系统交互的主要方式,除了通过数据库访问层与数据库进行通信外,它是服务与外部世界连接的桥梁。外部系统可以是网站、微服务或桌面应用程序等。

一个典型的 HTTP 响应通常包含以下三个关键部分:
- HTTP 状态码 :如 200(OK)、404(Not Found)或 500(Internal Server Error),用于表示请求处理的结果状态。
- 头部信息 :通常包含返回数据的类型和跨域资源共享(CORS)指令等。在大多数情况下,ASP.NET 可以自动处理这些信息。
- 响应体 :在适当的情况下,返回给客户端的数据通常以 JSON 字符串的形式存在,通常与 200(OK)状态码一起返回。某些 HTTP 状态码不允许返回数据,如 201 状态码表示 “No Content”。

3. 确定需要实现的控制器

我们需要根据与 FlyTomorrow 签订的合同中规定的端点来确定需要实现的控制器。这些端点包括:
- GET /Flight
- GET /Flight/{FlightNumber}
- POST /Booking/{FlightNumber}

通过分析这些端点中出现的实体,我们可以确定需要实现的控制器:
| 端点 | 所需控制器 |
| — | — |
| GET /Flight | FlightController |
| GET /Flight/{FlightNumber} | FlightController |
| POST /Booking/{FlightNumber} | BookingController |

由于合同中没有要求为机场和客户实体设置控制器端点,根据 “You ain’t gonna need it”(YAGNI)原则,我们不需要为这些实体实现控制器。

4. 实现 FlightController

首先,我们需要创建 FlightController FlightControllerTests 两个类。为了使 FlightController 能够返回 HTTP 数据并设置路由,它需要继承自 Controller 类。

public class FlightController : Controller
{
    // 后续代码将添加在这里
}

为了使外部系统能够访问我们的端点,我们需要实现以下三个部分:
- IActionResult 接口
- 中间件中的依赖注入
- 端点路由

4.1 使用 IActionResult 接口返回 HTTP 响应(GetFlights 方法)

在处理 HTTP 响应时,由于没有原始数据类型可以直接存储 HTTP 响应的所有信息,我们可以使用 ASP.NET 的 IActionResult 接口。这个接口是 ActionResult ContentResult 等类的起点,在实际使用中,我们可以将具体类的选择交给 CLR 处理,这体现了多态性和 “面向接口编程” 的原则。

下面是一个简单的示例,展示了如何使用 IActionResult 接口返回一个 HTTP 状态码为 200 和一个字符串 “Hello, World!” 的响应:

public IActionResult GetFlights()  
{
    return StatusCode(200, "Hello, World!");
}

为了避免使用硬编码的状态码,我们可以使用 HttpStatusCode 枚举:

public IActionResult GetFlights()  
{
    return StatusCode((int) HttpStatusCode.OK, "Hello, World!");
}

然而,我们的需求是返回数据库中所有航班的信息。为此,我们需要调用服务层的方法,并处理可能出现的异常。以下是更新后的 GetFlights 方法:

public async Task<IActionResult> GetFlights()
{
    try
    {
        Queue<FlightView> flights = new Queue<FlightView>();
        await foreach (FlightView flight in _service.GetFlights())
        {
            flights.Enqueue(flight);
        }

        return StatusCode((int)HttpStatusCode.OK, flights);
    }
    catch(FlightNotFoundException)
    {
        return StatusCode((int) HttpStatusCode.NotFound, "No flights were found in the database");
    }
    catch (Exception)
    {
        return StatusCode((int) HttpStatusCode.InternalServerError, "An error occurred");
    }
}
4.2 使用中间件将依赖项注入到控制器中

在之前的开发中,我们使用依赖注入来延迟实例化依赖项。现在,我们需要在中间件中实际实例化这些依赖项。中间件是用于处理 HTTP 请求的代码,可以将其看作是一系列按顺序执行的组件。

在 ASP.NET 服务中,中间件代码通常位于 Startup 类中。以下是一个简单的 Startup 类示例:

class Startup
{
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseRouting();
        app.UseEndpoints(endpoints =>  
        { endpoints.MapControllers(); });
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
    }
}

ConfigureServices 方法中,我们可以添加需要注入的依赖项。有三种类型的注入方式:
- 单例(Singleton) :在服务的整个生命周期内只有一个实例。
- 作用域(Scoped) :在每个请求的生命周期内只有一个实例。
- 瞬态(Transient) :每次使用依赖项时都会创建一个新的实例。

由于瞬态依赖项是最常见且易于使用的,我们将使用这种方式为 FlightController 添加 FlightService 依赖项:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddTransient(typeof(FlightService), typeof(FlightService));
    services.AddTransient(typeof(FlightRepository), typeof(FlightRepository));
    services.AddTransient(typeof(AirportRepository), typeof(AirportRepository));
    services.AddTransient(typeof(FlyingDutchmanAirlinesContext), typeof(FlyingDutchmanAirlinesContext));
}

现在,我们可以将注入的依赖项添加到 FlightController 中,并调用 FlightService

public class FlightController : Controller
{
    private readonly FlightService _service;

    public FlightController(FlightService service)
    {
        _service = service;
    }

    // 其他方法...
}
4.3 单元测试

为了确保 GetFlights 方法的正确性,我们需要编写单元测试。以下是一个完整的单元测试示例:

[TestClass]
public class FlightControllerTests
{
    [TestMethod]
    public async Task GetFlights_Success()
    {
        Mock<FlightService> service = new Mock<FlightService>();

        List<FlightView> returnFlightViews = new List<FlightView>(2)
        {
            new FlightView("1932",  ("Groningen", "GRQ"), ("Phoenix", "PHX")),
            new FlightView("841",  ("New York City", "JFK"), ("London", "LHR"))
        };

        service.Setup(s => s.GetFlights()).Returns(FlightViewAsyncGenerator(returnFlightViews));

        FlightController controller = new FlightController(service.Object);
        ObjectResult response = await controller.GetFlights() as ObjectResult;

        Assert.IsNotNull(response);
        Assert.AreEqual((int)HttpStatusCode.OK, response.StatusCode);

        Queue<FlightView> content = response.Value as Queue<FlightView>;
        Assert.IsNotNull(content);

        Assert.IsTrue(returnFlightViews.All(flight => content.Contains(flight)));
    }

    private async IAsyncEnumerable<FlightView> FlightViewAsyncGenerator(IEnumerable<FlightView> views)
    {
        foreach (FlightView flightView in views)
        {
            yield return flightView;
        }
    }
}

此外,我们还需要编写处理异常情况的单元测试,如 FlightNotFoundException ArgumentException

[TestMethod]
public async Task GetFlights_Failure_FlightNotFoundException_404()
{
    Mock<FlightService> service = new Mock<FlightService>();
    service.Setup(s => s.GetFlights()).Throws(new FlightNotFoundException());

    FlightController controller = new FlightController(service.Object);
    ObjectResult response = await controller.GetFlights() as ObjectResult;

    Assert.IsNotNull(response);
    Assert.AreEqual((int)HttpStatusCode.NotFound, response.StatusCode);
    Assert.AreEqual("No flights were found in the database", response.Value);
}

[TestMethod]
public async Task GetFlights_Failure_ArgumentException_500()
{
    Mock<FlightService> service = new Mock<FlightService>();
    service.Setup(s => s.GetFlights()).Throws(new ArgumentException());

    FlightController controller = new FlightController(service.Object);
    ObjectResult response = await controller.GetFlights() as ObjectResult;

    Assert.IsNotNull(response);
    Assert.AreEqual((int)HttpStatusCode.InternalServerError, response.StatusCode);
    Assert.AreEqual("An error occurred", response.Value);
}

通过以上步骤,我们完成了 FlightController 的实现和单元测试。但目前还无法从外部系统调用这个端点,后续将介绍如何设置路由。

总结

本文详细介绍了如何实现飞行荷兰人航空公司服务的控制器层,特别是 FlightController 。我们首先探讨了控制器在 Repository/Service 模式中的位置,然后确定了需要实现的控制器,接着详细介绍了 FlightController 的实现过程,包括使用 IActionResult 接口返回 HTTP 响应、使用中间件进行依赖注入以及编写单元测试。通过这些步骤,我们为服务的可用性迈出了重要的一步。后续将继续完成 BookingController 的实现,并进行路由设置和验收测试。

飞行荷兰人航空公司服务控制器层实现指南

5. 路由端点

现在我们已经完成了 FlightController 的实现和单元测试,但还不能从外部系统调用端点,接下来需要设置路由。

在 ASP.NET 中,路由是将 HTTP 请求映射到控制器和操作方法的过程。我们可以使用 HttpAttribute 方法属性来声明 HTTP 路由。

以下是如何为 GetFlights 方法设置路由的示例:

[HttpGet("Flight")]
public async Task<IActionResult> GetFlights()
{
    // 方法实现
}

在这个示例中, [HttpGet("Flight")] 属性指定了该方法将处理 GET /Flight 请求。

对于带有参数的请求,例如 GET /Flight/{FlightNumber} ,可以这样设置路由:

[HttpGet("Flight/{FlightNumber}")]
public async Task<IActionResult> GetFlightByNumber([FromRoute] string FlightNumber)
{
    // 方法实现
}

这里, [HttpGet("Flight/{FlightNumber}")] 指定了处理 GET /Flight/{FlightNumber} 请求, [FromRoute] 属性用于从路由中获取 FlightNumber 参数。

路由的执行流程可以用以下 mermaid 流程图表示:

graph LR
    A[HTTP 请求] --> B[路由匹配]
    B -->|匹配成功| C[调用控制器方法]
    B -->|匹配失败| D[返回 404 错误]
    C --> E[处理请求]
    E --> F[返回 HTTP 响应]
6. 总结与展望

通过前面的步骤,我们已经完成了 FlightController 的完整实现,包括返回 HTTP 响应、依赖注入、单元测试和路由设置。下面总结一下实现 FlightController 的关键步骤:
1. 创建控制器类 :继承自 Controller 类。
2. 使用 IActionResult 接口 :返回 HTTP 响应。
3. 依赖注入 :在中间件中配置依赖项。
4. 编写单元测试 :确保方法的正确性。
5. 设置路由 :将 HTTP 请求映射到控制器方法。

接下来,我们将实现 BookingController 以处理 POST /Booking/{FlightNumber} 请求。以下是实现 BookingController 的大致步骤列表:
1. 确定控制器 :根据端点确定需要实现 BookingController
2. 创建类和测试类 :创建 BookingController BookingControllerTests 类。
3. 依赖注入 :注入 BookingService 等依赖项。
4. 实现方法 :实现处理 POST /Booking/{FlightNumber} 请求的方法。
5. 编写单元测试 :确保方法的正确性。
6. 设置路由 :为方法设置合适的路由。

最后,我们将进行验收测试,使用 Swagger 来验证服务是否符合 API 规范。通过这些步骤,我们将完成飞行荷兰人航空公司服务的完整实现,使其能够与外部系统进行交互。

步骤 描述
1 创建 BookingController BookingControllerTests
2 在中间件中注入 BookingService 等依赖项
3 实现处理 POST /Booking/{FlightNumber} 请求的方法
4 编写单元测试确保方法正确性
5 为方法设置路由
6 使用 Swagger 进行验收测试

通过本文的介绍,希望你对控制器层的实现有了更深入的理解,能够顺利完成飞行荷兰人航空公司服务的开发。

【四轴飞行器】非线性三自由度四轴飞行器模拟器研究(Matlab代码实现)内容概要:本文围绕非线性三自由度四轴飞行器的建模与仿真展开,重点介绍了基于Matlab的飞行器动力学模型构建与控制系统设计方法。通过对四轴飞行器非线性运动方程的推导,建立其在三维空间中的姿态与位置动态模型,并采用数值仿真手段实现飞行器在复杂环境下的行为模拟。文中详细阐述了系统状态方程的构建、控制输入设计以及仿真参数设置,并结合具体代码实现展示了如何对飞行器进行稳定控制与轨迹跟踪。此外,文章还提到了多种优化与控制策略的应用背景,如模型预测控制、PID控制等,突出了Matlab工具在无人机系统仿真中的强大功能。; 适合人群:具备一定自动控制理论基础和Matlab编程能力的高校学生、科研人员及从事无人机系统开发的工程师;尤其适合从事飞行器建模、控制算法研究及相关领域研究的专业人士。; 使用场景及目标:①用于四轴飞行器非线性动力学建模的教学与科研实践;②为无人机控制系统设计(如姿态控制、轨迹跟踪)提供仿真验证平台;③支持高级控制算法(如MPC、LQR、PID)的研究与对比分析; 阅读建议:建议读者结合文中提到的Matlab代码与仿真模型,动手实践飞行器建模与控制流程,重点关注动力学方程的实现与控制器参数调优,同时可拓展至多自由度或复杂环境下的飞行仿真研究。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值