gpt4 book ai didi

ASP.NET Web API 契约(Contract)版本控制

转载 作者:行者123 更新时间:2023-12-03 17:39:44 26 4
gpt4 key购买 nike

我们希望在接受 header 中使用内容协商来实现基于版本的 API。

我们能够通过一些继承和扩展默认的 HTTP 选择器来实现 Controller 和 API 方法。

使用以下示例代码实现 Controller 继承,

public abstract class AbstractBaseController : ApiController
{
// common methods for all api
}

public abstract class AbstractStudentController : AbstractBaseController
{
// common methods for Student related API'sample

public abstract Post(Student student);
public abstract Patch(Student student);
}

public class StudentV1Controller : AbstractStudentController
{
public override Post([FromBody]Student student) // student should be instance of StudentV1 from JSON
{
// To Do: Insert V1 Student
}

public override Patch([FromBody]Student student) // student should be instance of StudentV1 from JSON
{
// To Do: Patch V1 Student
}
}

public class StudentV2Controller : AbstractStudentController
{
//
public override Post([FromBody]Student student) // student should be instance of StudentV2 from JSON
{
// To Do: Insert V2 Student
}
}

public abstract class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
}

public class StudentV1 : Student
{
}

public class StudentV2 : Student
{
public string Email { get; set; }
}

我们已经创建了上述架构,以减少版本更改的代码,例如,如果版本 1 有 10 个 API 方法并且其中一个 API 方法发生了变化,那么它应该在版本 2 代码中可用而不修改其他 9 个(它们是从版本继承而来的) 1)。

现在,我们面临的主要问题是合约版本控制,因为我们无法实例化抽象学生的实例。当有人将 JSON 发布到 API 版本 1 时,StudentV1 实例应该在方法中传递,并且在版本 2 中相同。

有没有办法实现这一目标?

提前致谢!!

最佳答案

ASP.NET API Versioning能够实现您的目标。首先,您需要添加对ASP.NET Web API API VersioningNuGet 包的引用。
然后,您将配置您的应用程序,例如:

public class WebApiConfig
{
public static void Configure(HttpConfiguration config)
{
config.AddApiVersioning(
options => options.ApiVersionReader = new MediaTypeApiVersionReader());
}
}
您的 Controller 可能类似于:
namespace MyApp.Controllers
{
namespace V1
{
[ApiVersion("1.0")]
[RoutePrefix("student")]
public class StudentController : ApiController
{
[Route("{id}", Name = "GetStudent")]
public IHttpActionResult Get(int id) =>
Ok(new Student() { Id = id });

[Route]
public IHttpActionResult Post([FromBody] Student student)
{
student.Id = 42;
var location = Link("GetStudent", new { id = student.Id });
return Created(location, student);
}

[Route("{id}")]
public IHttpActionResult Patch(int id, [FromBody] Student student) =>
Ok(student);
}
}

namespace V2
{
[ApiVersion("2.0")]
[RoutePrefix("student")]
public class StudentController : ApiController
{
[Route("{id}", Name = "GetStudentV2")]
public IHttpActionResult Get(int id) =>
Ok(new Student() { Id = id });

[Route]
public IHttpActionResult Post([FromBody] StudentV2 student)
{
student.Id = 42;
var location = Link("GetStudentV2", new { id = student.Id });
return Created(location, student);
}

[Route("{id}")]
public IHttpActionResult Patch(int id, [FromBody] StudentV2 student) =>
Ok(student);
}
}
}
强烈反对继承。这是可能的,但这是解决 IMO 问题的错误方法。 API 和 HTTP 都不支持继承。这是支持语言的实现细节,也有点阻抗不匹配。一个关键问题是您不能取消继承方法,因此也不能取消 API。
如果你真的坚持继承。选择以下选项之一:
  • 只有的基类 protected成员
  • 将业务逻辑移出 Controller
  • 使用扩展方法或其他合作者完成共享操作

  • 例如,您可能会执行以下操作:
    namespace MyApp.Controllers
    {
    public abstract class StudentController<T> : ApiController
    where T: Student
    {
    protected virtual IHttpActionResult Get(int id)
    {
    // common implementation
    }

    protected virtual IHttpActionResult Post([FromBody] T student)
    {
    // common implementation
    }

    protected virtual IHttpActionResult Patch(int id, [FromBody] Student student)
    {
    // common implementation
    }
    }

    namespace V1
    {
    [ApiVersion("1.0")]
    [RoutePrefix("student")]
    public class StudentController : StudentController<Student>
    {
    [Route("{id}", Name = "GetStudentV1")]
    public IHttpActionResult Get(int id) => base.Get(id);

    [Route]
    public IHttpActionResult Post([FromBody] Student student) =>
    base.Post(student);

    [Route("{id}")]
    public IHttpActionResult Patch(int id, [FromBody] Student student) =>
    base.Patch(student);
    }
    }

    namespace V2
    {
    [ApiVersion("2.0")]
    [RoutePrefix("student")]
    public class StudentController : StudentController<StudentV2>
    {
    [Route("{id}", Name = "GetStudentV2")]
    public IHttpActionResult Get(int id) => base.Get(id);

    [Route]
    public IHttpActionResult Post([FromBody] StudentV2 student) =>
    base.Post(student);

    [Route("{id}")]
    public IHttpActionResult Patch(int id, [FromBody] StudentV2 student) =>
    base.Patch(student);
    }
    }
    }
    还有其他方法,但这只是一个例子。如果您定义了一个合理的版本控制策略(例如:N-2 个版本),那么重复的数量是最少的。继承可能会导致比它解决的问题更多的问题。
    当您按媒体类型进行版本控制时,默认行为使用 v媒体类型参数来指示 API 版本。如果您愿意,您可以更改名称。其他形式的媒体类型版本控制也是可能的(例如: application/json+student.v1,您需要一个自定义的 IApiVersionReader,因为没有标准格式。此外,您必须更新 ASP.NET MediaTypeFormatter 配置中的映射。内置媒体类型映射不考虑媒体类型参数(例如 v参数没有影响)。
    下表显示了映射:


    方法
    标题
    例子

    GET Accept application/json;v=1.0 PUT Content-Type application/json;v=1.0 POST Content-Type application/json;v=1.0 PATCH Content-Type application/json;v=1.0 DELETE AcceptContent-Type application/json;v=1.0
    DELETE是一个异常情况,因为它不需要输入或输出媒体类型。 Content-Type将始终优先于 Accept,因为它是主体所必需的。 DELETEAPI 可以设为 API 版本中立,这意味着将采用任何 API 版本,包括根本没有。如果您想在不需要媒体类型的情况下允许 DELETE,这可能很有用。另一种替代方法是使用媒体类型和查询字符串版本控制方法。这将允许在 DELETEAPI 的查询字符串中指定 API 版本。
    在电线上,它看起来像:
    要求
    POST /student HTTP/2
    Host: localhost
    Content-Type: application/json;v=2.0
    Content-Length: 37

    {"firstName":"John","lastName":"Doe"}
    回复
    HTTP/2 201 Created
    Content-Type: application/json;v=2.0
    Content-Length: 45
    Location: http://localhost/student/42

    {"id":42,"firstName":"John","lastName":"Doe"}

    关于ASP.NET Web API 契约(Contract)版本控制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39371014/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com