RESTAPI方法的复合键

RESTAPI方法的复合键,api,rest,Api,Rest,我正在为以下用例寻找RESTful API设计的最佳实践: 域对象车辆: class Vehicle { private String vehicleType; private String colour; private String transmission; private String yearOfIssue; } 一个示例对象: Vehicle = {vehicleType : 'Car', colour : 'Red', transmission :

我正在为以下用例寻找RESTful API设计的最佳实践:

域对象车辆:

class Vehicle {
    private String vehicleType;
    private String colour;
    private String transmission;
    private String yearOfIssue;
}
一个示例对象:

Vehicle = {vehicleType : 'Car', colour : 'Red', transmission : 'Automatic', yearOfIssue : '2008'};
在此域模型中,没有单个字段唯一标识符(例如vehicleId),而是对象的所有字段一起构成主键(此约束在数据库层中)

我们无法灵活地更改此域模型以添加单个字段唯一标识符

所以我的问题如下-如果我想在这个域对象上添加一个简单的RESTAPI,它提供创建、更新、删除和获取车辆的简单功能,那么这些方法的路径端点的最佳实践是什么

根据上面的示例,如果域模型具有单个字段唯一标识符vehicleId,那么我可以想象以下端点:

GET /vehicles/:vehicleId
PUT /vehicles/:vehicleId
DELETE /vehicles/:vehicleId
我不知道复合键存在类似的模式,如:

GET /vehicles/:vehicleTypecolourtransmissionyearOfIssue
GET /vehicles/CarRedAutomatic2008
似乎不正确

如果您对本用例遵循的良好模式有任何建议,我们将不胜感激


谢谢

根据通用REST标准,每个端点都公开一个资源,客户端可以使用http谓词处理它们。在本例中,您的资源是车辆,客户机使用GET从服务器获取数据。理想情况下,每个资源都应该使用唯一(单个)密钥进行唯一标识

但是您的资源(车辆)没有单一值唯一密钥,并且无法在系统中更改!在这种情况下,您仍然可以使用所有必需的参数进行GET调用,以标识资源,就像任何其他标准http调用一样,如

GET /vehicles?type=Car&color=Red&transmission=Automatic&manufactureYear=2008
您正在使用的技术/平台,如果允许为您的方法创建自定义路由,您可以创建自定义路由,如

new route("/vehicles/{type}/{color}/{transmission}/{manufactureYear}")
并将您的服务呼叫为

GET /vehicles/Car/Red/Automatic/2008

这样做的好处是,uri变短了。但另一方面[1]对于这种类型的所有方法/资源,您必须创建自定义路由,[2]除非您了解特定的方法和路由,否则此uri没有多大意义。

要实现RESTful,您需要创建一个唯一的标识符来扩展类

class Vehicle {
    public int vechicleId { get; set; }
    public string vehicleType { get; set; }
    public string colour { get; set; }
    public string transmission { get; set; }
    public string yearOfIssue { get; set; }
}
然后您可以使用HTTP:Get访问它。但是,您可能无法访问内部唯一标识符,尤其是在对数据库进行种子设定或更新时。我遇到过类似的问题,为了使用REST动词,我将包括一个外部标识符,以便于人类和外部系统访问记录:

class Vehicle {
    public int vechicleId { get; set; }
    public string externalId { get; set; }
    public string vehicleType { get; set; }
    public string colour { get; set; }
    public string transmission { get; set; }
    public string yearOfIssue { get; set; }
}
然后这个动词看起来像: HTTP:获取。您不必解析URI,因为所有数据都应该在消息体中,您只需要确保字符串唯一地标识车辆

[HttpPut("externalId/{externalId}")]
public IActionResult PutVehicle([FromRoute] string externalId, [FromBody] JObject jObject)
{
    // See if the record exists already.
    var oldVehicle = (from v in vehicles
                       where vehicle.ExternalId == externalId
                       select v).FirstOrDefault();
    if (oldVehicle != null)
    {
        <insert new vehicle>
    }
    else
    {
        <update old vehicle>
    }
[HttpPut(“externalId/{externalId}”)]
public IActionResult PutVehicle([FromRoute]字符串外部ID,[FromBody]作业对象作业对象)
{
//查看记录是否已经存在。
var oldVehicle=(来自车辆中的v
其中vehicle.ExternalId==ExternalId
选择v).FirstOrDefault();
如果(旧车辆!=空)
{
}
其他的
{
}

在ASP.NET Core中,我通常表示这样的复合键:

POST/vehicles/(汽车:红色:自动:2008)

POST/vehicles/(汽车|红色|自动| 2008)

框架可以按照指定的顺序将这些参数映射到操作参数

[HttpPut("vehicles/({car}:{color}:{trans}:{year})")]
public async Task<IActionResult> Add(
    string car, string color, string trans, int year, [FromBody] Vehicle request)
{
    await Task.CompletedTask;

    return Ok();
}
[HttpPut(“车辆/({car}:{color}:{trans}:{year})])
公共异步任务添加(
字符串车,字符串颜色,字符串变速器,整数年,[FromBody]车辆请求)
{
等待任务。完成任务;
返回Ok();
}
请求示例:
PUT/vehicles/(福特:Ranger:100%正品:2000)


使用path比使用params来指定密钥更好,因为它可以与任何动词一起使用。例如,您可以使用
POST/vehicles/Car/Red/Automatic/2008
@JohnHenckel进行更新。根据,查询参数是URL的一部分,因此您也应该能够发布到它们。参见我想看到更大的问题已解决:OP的任务是否可以使用REST完成,或者它只是一个标准的HTTP Get?我想如果没有一个唯一的标识符,它就不是REST。@DonaldAirey看到了我的答案,类似于一个唯一标识符。这是一个弱解决方案,因为它假定数据不包含任何特殊字符。例如,someo请插入一辆变速器为“100%正版”的汽车,然后ASP.NET将抛出一个异常。它不允许在路径中使用特殊字符。@JohnHenckel此答案专门针对
ASP.NET Core
(如上所述),并且该框架允许使用特殊字符。请参阅添加到答案中的屏幕截图。