Vij*_*han 5 versioning asp.net asp.net-mvc asp.net-web-api asp.net-web-api2
我们希望在accept头中使用内容协商来实现基于版本的API.
我们能够通过一些继承实现控制器和API方法,并扩展默认的HTTP选择器.
使用以下示例代码实现控制器继承,
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; }
}
Run Code Online (Sandbox Code Playgroud)
我们已经创建了上面的体系结构,以便在版本更改的情况下执行更少的代码,比如说版本1有10个API方法,并且一个API方法的更改比版本2代码中可用的更改而不修改其他9个(它们是从版本继承的) 1).
现在,我们面临的主要问题是合同版本控制,因为我们无法实例化抽象学生的实例.当有人将JSON发布到API版本1时,应该在方法中传递StudentV1的实例,并在版本2中传递相同的实例.
有没有办法实现这个目标?
提前致谢!!
ASP.NET API 版本控制能够实现您的目标。首先,您需要添加对ASP.NET Web API API 版本控制NuGet 包的引用。
然后,您将配置您的应用程序,例如:
public class WebApiConfig
{
public static void Configure(HttpConfiguration config)
{
config.AddApiVersioning(
options => options.ApiVersionReader = new MediaTypeApiVersionReader());
}
}
Run Code Online (Sandbox Code Playgroud)
您的控制器可能类似于:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我强烈建议不要继承。这是可能的,但在我看来,这是解决问题的错误方法。API 和 HTTP 都不支持继承。这是支持语言的实现细节,这也有点阻抗不匹配。一个关键问题是您无法取消继承方法,因此也无法取消继承 API。
如果你真的坚持继承的话。选择以下选项之一:
protected包含成员的基类例如,您可能会这样做:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
还有其他方法,但这只是一个例子。如果您定义了合理的版本控制策略(例如: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 |
Accept或者Content-Type |
application/json;v=1.0 |
DELETE是一种异常情况,因为它不需要输入或输出媒体类型。Content-Type总是优先于它,Accept因为它是身体所必需的。APIDELETE可以成为 API版本中立的,这意味着可以采用任何 API 版本,包括根本没有版本。如果您想允许DELETE而不需要媒体类型,这可能很有用。另一种替代方法是使用媒体类型和查询字符串版本控制方法。这将允许在 API 的查询字符串中指定 API 版本DELETE。
通过网络,它看起来像:
POST /student HTTP/2
Host: localhost
Content-Type: application/json;v=2.0
Content-Length: 37
{"firstName":"John","lastName":"Doe"}
Run Code Online (Sandbox Code Playgroud)
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"}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
479 次 |
| 最近记录: |