Компонентный ключ для методов API REST

Я ищу лучшие практики в дизайне API RESTful для следующего использования:

Объект объекта домена:

class Vehicle {
    private String vehicleType;
    private String colour;
    private String transmission;
    private String yearOfIssue;
}

Пример объекта:

Vehicle = {vehicleType : 'Car', colour : 'Red', transmission : 'Automatic', yearOfIssue : '2008'};

В этой модели домена нет единого уникального идентификатора поля (например, vehicleId), но все поля объекта вместе образуют первичный ключ (это ограничение присутствует в слое базы данных).

У нас нет гибкости для изменения этой модели домена, чтобы добавить уникальный уникальный идентификатор поля.

Итак, мой вопрос следующий: если я хочу добавить простой REST API поверх этого объекта домена, который обеспечивает простую функциональность для CREATE, UPDATE, DELETE и GET Vehicles, то что лучше всего подходит для конечных точек PATH для этих методы?

Следуя приведенному выше примеру, если модель домена должна иметь уникальный идентификатор транспортного средства с идентификатором поля, тогда я могу представить следующие конечные точки:

GET /vehicles/:vehicleId
PUT /vehicles/:vehicleId
DELETE /vehicles/:vehicleId

Мне не известно о шаблоне, который аналогичен этому для составных клавиш:

GET /vehicles/:vehicleTypecolourtransmissionyearOfIssue
GET /vehicles/CarRedAutomatic2008

кажется неправильным.

Приветствуются любые советы по хорошему шаблону для этого случая использования.

Спасибо

Ответ 1

В соответствии с общими стандартами 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] этот ури не имеет особого смысла, если вы не знаете о конкретном методе и маршруте.

Ответ 2

Чтобы быть 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: Получить http://mysite/vehicles/3842. Однако у вас может не быть доступа к внутреннему уникальному идентификатору, особенно при посеве или обновлении базы данных. Я столкнулся с подобными проблемами, и для использования глаголов 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: Получить http://mysite/vehicles/externalId/sedanbluemanual2015. Вам не нужно анализировать 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>
    }

Ответ 3

В ядре ASP.NET я обычно представляю составной ключ следующим образом:

POST/vehicles/(car:red:automatic:2008)

или же

POST/vehicles/(car|red|automatic|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();
}

Пример запроса: PUT/vehicles/(Ford:Ranger:100% genuine:2000)

enter image description here