Макет URL для многоязычной CMS

Итак, я пытаюсь добавить новый блестящий веб-сервис REST в нашу CMS. Он должен довольно точно следовать «правилам» REST, поэтому он должен использовать GET / POST / PUT / DELETE и правильные, логические URL-адреса. Мой дизайн в значительной степени вдохновлен Лучшими практиками Apigee.

CMS управляет деревом категорий, где каждая категория может содержать несколько статей. Для многоязычных проектов категории дублируются для каждого языка (поэтому любая статья уникально идентифицируется своим идентификатором и идентификатором языка). Структура одинакова для всех языков, только позиции могут различаться (поэтому категория X содержит категории Y и Z на каждом языке, но Y может быть перед Z на языке 1 и наоборот, в язык 2). При создании новой статьи или категории всегда создаются копии на всех языках. Удаление работает одинаково, поэтому статья всегда удаляется на всех языках.

К вашему сведению: языки идентифицируются по числовому идентификатору или языку (например, en_US).

(Представьте начальный /v1 перед всеми URI; я пропустил его, потому что он ничего не добавляет к этому вопросу.) `

В моем текущем подходе используется такая схема URL-адресов:

GET    /articles               :(  returns all articles in all languages
GET    /articles/:id-:langid   :)  returns a single article in a given language
POST   /articles               :)  creates a new article
PUT    /articles/:id-:langid   :)  update a single article in a given language
DELETE /articles/:id           :)  delete an article in all languages

Но...

Как определить языки?

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

Было бы неплохо использовать языковой стандарт (скорее всего, в нижнем регистре), потому что URL-адреса более читабельны. Но это было бы

  • может привести к проектам с пересекающимися регионами.
  • требовать, чтобы клиенты сначала получали локали (но я предполагаю, что в противном случае клиенту все равно пришлось бы получать идентификаторы языков, так что это вроде нормально).

Я хотел бы прийти к единственному решению этой проблемы и склоняться к использованию локали. Но стоит ли рисковать пересечением локаций?

(Вы можете заменить изображение langid=X на locale=xx_xx в дальнейшем.)

Должен ли язык быть элементом иерархии?

В большинстве случаев клиенты API захотят получить статьи для данного языка, а не для всех. Я мог бы решить эту проблему, разрешив параметр GET и предложив GET /articles?langid=42. Но поскольку это такой распространенный вариант использования, я бы хотел избежать необязательного параметра и сделать его явным.

Это привело бы к GET /articles/:langid, но здесь вводится концепция, согласно которой язык является уровнем иерархии внутри API. Если я собираюсь это сделать, я хотел бы сделать это последовательным для других глаголов. Новый макет URL-адреса будет выглядеть так:

GET    /articles/:langid       :)  returns all articles in a given language
GET    /articles/:langid/:id   :)  returns a single article in a given language
POST   /articles               :(  creates a new article in all languages
PUT    /articles/:langid/:id   :)  update a single article in a given language
DELETE /articles/:langid/:id   :(  delete an article in all languages

or

GET    /:langid/articles       :)  returns all articles in a given language
GET    /:langid/articles/:id   :)  returns a single article in a given language
POST   /articles               :(  creates a new article in all languages
PUT    /:langid/articles/:id   :)  update a single article in a given language
DELETE /:langid/articles/:id   :(  delete an article in all languages

Это ведет к...

Что делать с глобальными действиями?

Проблема с указанным выше макетом состоит в том, что POST и DELETE работают на всех языках, но URL-адрес предполагает, что они работают только на одном языке. Итак, я мог изменить макет:

GET    /articles/:langid       :)  returns all articles in a given language
GET    /articles/:langid/:id   :)  returns a single article in a given language
POST   /articles               :)  creates a new article in all languages
PUT    /articles/:langid/:id   :)  update a single article in a given language
DELETE /articles/:id           :)  delete an article in all languages

Это создает несколько запутанную компоновку, поскольку языковой уровень присутствует только иногда, и нужно много знать о внутреннем устройстве системы. С другой стороны, это очень хорошо сочетается с системой и очень похоже на внутреннюю работу.

Жертвоприношения?

Так где же мне принести жертвы? Должен ли я вводить псевдонимы URL-адресов, чтобы иметь красивую разметку URL-адресов, но, возможно, делать непредвиденные вещи в фоновом режиме?


person xrstf    schedule 01.11.2012    source источник


Ответы (2)


Некоторое время назад у меня была точно такая же проблема, и я решил поставить перед всем локальное. При потреблении контента имеет смысл читать такие URL-адреса, как:

/en/articles/1/

/en/authors/2/

/gr/articles/1/

/gr/authors/2/

чем

/articles/en/1/

/authors/en/2/

/articles/gr/1/

/authors/gr/2/

Чтобы решить проблему «отсутствия конкретного языкового стандарта», я бы использовал ключевое слово, относящееся ко всем доступным языкам, или языковой стандарт по умолчанию. Так:

/global/articles/

or

/all-locales/articles/

or

/all/articles/

честно говоря, мне нравятся глобальные и все такое, потому что они имеют смысл их читать.

DELETE /global/articles/:id :) delete an article in all languages

надеюсь я помог

person PetrosZ    schedule 08.07.2013

Мой первоначальный подход заключался в том, чтобы указать язык в качестве необязательного префикса в URL-адресе, например /en_us/articles, /en_us/articles/:id, но если вы не хотите «засорять» свои URL-адреса языковыми идентификаторами, вы можете вместо этого использовать заголовок Accept-language точно так же, как согласование содержимого определено в RFC 2616. Microsoft WebAPI использует этот подход для согласования формата.

person gnz    schedule 02.11.2012