LINUX.ORG.RU

В каких web-фреймфорках самый удобный и красивый роутинг?

 ,


0

2

Думаю тут, что мой формат достаточно устарел, не всегда удобен. Рассматриваю вариант упрощения и улучшения. Понятно, что лучше подсмотреть, как с этим в других вариантах.

Навскидку самый красивый и удобный роутинг — у PlayFramework:

*   /clients/{id}             Clients.show   
GET /clients/{id}/accounts/{accountId} Clients.blah.blah

Правда, немного некрасиво становится при использовании регекспов:

/clients/{<[a-z]{4,10}>id}

У Django роутинг просто ужасен:

urlpatterns = patterns('',
    (r'^articles/2003/$', 'news.views.special_case_2003'),
    (r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
    (r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', 'news.views.month_archive'),
    (r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$', 'news.views.article_detail'),
)

Или я что-то упустил и есть какие-нибудь более приличные ныне надстройки?

Вариант RoR не сильно лучше.

match "/:year(/:month(/:day))" => "info#about", :constraints => 
{ :year => /\d{4}/, :month => /\d{2}/, :day => /\d{2}/ }

Какие ещё есть популярные варианты?

★★★★★

Кстати, вопрос ещё и о терминологии. Я сейчас оперирую терминами «url maps». Думаю, оправдан ли будет переход на «routes» в плане более привычных терминов? Или единообразия тут нет?

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

Пишу фреймворк, тебе бы понравилось такое?

route /"read"/classOf[Book] -> { book=>

}

Книгу само берет из базы конвертируя из ID, но можно /«read»/int ->

Статическая типизация, тип book - Book

Написать сокращение вот так

val rRead = route /"read"

rRead /classOf[Book] ...
vertexua ★★★★★
()

У Django роутинг просто ужасен:

Это если ты named-регэкспы используешь. Без них строки выглядят всяко красивее:)

anonymous
()
Ответ на: комментарий от KRoN73

Кстати, вопрос ещё и о терминологии. Я сейчас оперирую терминами «url maps». Думаю, оправдан ли будет переход на «routes» в плане более привычных терминов? Или единообразия тут нет?

как-то больше общепринято: роутинг/руты

в kohana framework похож на RoR

Route::set('gallery', '<action>(<controller>):<id>',
  array(
    'controller' => '[A-Z][a-z]++',
    'action'     => '[A-Z][a-z]++',
  ))
  ->defaults(array(
    'controller' => 'Slideshow',
  ));

в Yii, на котором сейчас пишу роутинг задается в параметре urlManager'а при подключении:

            'rules'=>array(
                '<language:\w{2}>' => 'site/index',
                '<language:\w{2}>/article/<alias:\w+>.html' => 'article/show',
                '<language:\w{2}>/<_c:\w+>' => '<_c>',
                '<language:\w{2}>/<_c:\w+>/<_a:\w+>'=>'<_c>/<_a>',
                '<language:\w{2}>/<_m:\w+>' => '<_m>',
                '<language:\w{2}>/<_m:\w+>/<_c:\w+>' => '<_m>/<_c>',
                '<language:\w{2}>/<_m:\w+>/<_c:\w+>/<_a:\w+>' => '<_m>/<_c>/<_a>',
            ),

BaBL ★★★★★
()
Ответ на: комментарий от vertexua

Пишу фреймворк, тебе бы понравилось такое?

Не думаю. Много лишнего :)

Сейчас у меня так (чистый PHP):

bors_url_map(array(
    '(/_bors/tools)(/.*) => include(bors_tools)',
    '/sitemap-(\w+)-(\d+).xml => bors_system_sitemap_map(1,2)',
));

Плюс опциональный автоматический роутинг для классов, имя которых можно вычислить из ссылки. Т.е., если не нашли ничего в роутинге, то по ссылке /books/123 будет искаться класс my_project_book(123). Или для /directory/planes/1234/monitoring/ ищется my_project_planes_monitoring(1234)

Вопрос автоматического роутинга — отдельный, а по первому варианту не нравится усложнённая работа с более чем двумя параметрами (у меня условно первый всегда id, второй — page). В иных случаях синтаксис усложняется. Плюс в случае мультипроектного сайта (когда работают вместе несколько репозиториев) происходит пересканирование всех файлов роутинга в каждом проекте, что хоть и копеечное, но снижает производительность.

Подумываю о создании файла роутинга своего формата, ближе всего — к варианту play framework (синтаксис регекспов только почище сделать). Если ничего лучше не найду :) Затем и тема.

Ну и компилировать все роутинги домена в один монолитный кеш-файл, сверяя только даты модификации при старте.

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от vertexua

И да, фреймворки с регексами - на металолом

А как ты форматы ID разделять планируешь?

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от BaBL

в kohana framework похож на RoR

Ага, спасибо. Кохану я не смотрел. Синтакис явно избыточный получается, да.

в Yii, на котором сейчас пишу

Угу, его я видел. Не понравилось :)



Кстати, а как в разных фреймворках решается (если решается) вопрос include суброутингов? Скажем, если очень много привязок в /admin/, то вынести в отдельный файл и прописывать там роутинг относительный. Чтобы глаза в общем не мозолило и меньше проверок делать при поиске нужного.

KRoN73 ★★★★★
() автор топика

В Grails, вроде, так:

name showFestival: "/$id/$type?/$name?" {
    controller = "festival"
    action = "show"
}

KRoN73 ★★★★★
() автор топика
config.add_route('idea', 'ideas/{idea}', view='mypackage.views.idea_view')
config.add_route('user', 'users/{user}', view='mypackage.views.user_view')
config.add_route('tag', 'tags/{tags}', view='mypackage.views.tag_view')

пример из pyrimad документации. Вполне удобно.

xpahos ★★★★★
()
Ответ на: комментарий от xpahos

Вполне удобно

Много писанины.
config.add_route ... idea/ideas/idea/idea … view/views/view — этим всем должен фреймворк заниматься. «Machines should work; people should think» © IBM

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от vertexua

Что именно?

route, кавычки, скобки, classOf…

Почему не:

[br]/read/{id} book[br]

?

Не говоря о том, что такие правила, как я выше упоминал, вообще автоматически могут резольвиться.

KRoN73 ★★★★★
() автор топика
Последнее исправление: KRoN73 (всего исправлений: 1)
Ответ на: комментарий от KRoN73
/                        frontpage
/forum                   forumlist
    /$id                 topic
        /$msg            message
            /$action     messageEdit

-- или

        /$msg/$action?            message.$action

/articles/($title-$id)?  articles.$id   

т.е. ты просто определись что тебя раздражает

- лишняя писанина? - наследуй префиксы

- ненравится кучерявые режекспы в url - так они выводятся из типов\формата ожидаемого результата (аннотаций)

- многобукв? вводи соглашение трансляции url в имя обработчика (то что ты зоавешь авторесолв)

Deleted
()
Ответ на: комментарий от Deleted

т.е. ты просто определись что тебя раздражает

Для того и тему завёл :)

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от xpahos

тогда нужно сделать биндинги для libastral.so

/ideas/{id}    myproject.ideas.view

В каком месте тут нужен libastral? :)

У меня оно, собственно, почти в таком виде сейчас и работает

'/ideas/(\d+) =>  myproject_ideas_view(1)',

А на практике даже и этого не нужно. Фреймворк, не найдя ссылки /ideas/1234 в роутинге сам ищет класс myproject.ideas.view(1234) и смотрит, разрешён ли в нём автороутинг. Я выше писал об этом.

А вот «config.add_route('idea', 'ideas/{idea}', view='mypackage.views.idea_view')» — это издевательство над программистом :)

KRoN73 ★★★★★
() автор топика
Последнее исправление: KRoN73 (всего исправлений: 3)

У всех свои лисапеды. Чтобы нормально читалось, мы в ямле храним.

Так для 1 компонента: https://github.com/nodeca/nodeca.forum/blob/master/config/router.yml

А так несколько компонентов монтируются в общее приложение и вешаются на домены https://github.com/nodeca/nodeca/blob/master/config/application.yml.example#L64 . Включая подвыверты, когда главную страницу форума надо не просто поменять, а вообще заоверрайдить за пределы точки монтирования.

~ а ля рельсовсике engines, но со своей спецификой.

Vit ★★★★★
()
Ответ на: комментарий от swwwfactory

yaml зачетная вещица

У меня он интенсивно используется для описания автогенерируемых классов:

extends: bors_catalogue_xls
auto_map: true
config_class: aviaport_admin_digest_origins_config

main_class: aviaport_digest_origin

export_fields:
    Источник:   title
    Тип:        type
    Ссылка:     www
    ID компании:    firm_id

Но для роутинга он (на мой вкус) явно избыточен :)

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

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

Там можно хранить целиком объект класса - не пробовал?

Но для роутинга он (на мой вкус) явно избыточен :)

Это да - проще описать в модулях, но когда не хочется лезть в модули - то в конфиги. Лучше конфигов в yaml не встречал.

export_fields:
    Источник:   title
    Тип:        type
    Ссылка:     www
    ID компании:    firm_id

Ты ключи не-ascii в полный рост используешь?

swwwfactory ★★
()

А чем рельсовый роутинг не нравится? По моему с точки зрения следования принципу DRY он самый лучший.

anonymous
()
Ответ на: комментарий от swwwfactory

Там можно хранить целиком объект класса - не пробовал?

Бэкенд (storage в терминах моего движка) на YAML реализуется легко, и в R/O-варианте даже есть заготовка, но на практике не требовался. Обычно либо БД нужна (поиск, индексы), либо парсинг сторонних CSV (как объектов), XML или своего markdown/bb-code формата страниц сайта в plain-text.

Лучше конфигов в yaml не встречал.

Будешь смеяться, но конфиги у меня обычно либо php (config_set('name', 'value')), либо… .ini :)
[section]
name = value

Я много с конфигами экспериментировал и именно к .ini как к основному формату и пришёл. Быстро парсится, хорошо смотрится, а деревья в конфиге мне пока не нужны были.

Ты ключи не-ascii в полный рост используешь?

Угу. Выводимые кодировки, кодировки БД, файлов — это всё настраивается. Но вот кодировка исходников (в т.ч. YAML/HAML/HTML-шаблонов) жёстко оговорена в UTF-8. Так что с этой стороны подвоха ждать не приходится. А в остальном — работает :) Что в pecl-yaml, что, когда его нет, в модуле от Symphony.

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

А, вообще, использование глобальных конфигов оказалсь порочной практикой, так что я их использование понемногу сворачиваю. Только чисто хостовые настройки, типа доступа к БД, корневых доменов и т.п.

А то в свете Web-фреймворки: где есть и как реализована (и как называется) «мультипроектность»? стали при смешении проектов конфликты вылезать.

Скажем, задан корень сайта как config_set('main_site_url', 'http://my-site.tld'). И используется в классах url, генерируемые от этого параметра. Потом, бац, подключаем к работе второй проект, с таким же именем переменной. И иди потом разруливай внешние ссылки на объекты :)

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

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

Тем более, что есть гибкие классы-конфигураторы, так что часто в классе проекта достаточно указать только класс его конфигуратора. Так часто он из двух строчек на yaml и состоит:

extends: bors_page
config_class: aviaport_admin_config

А уже оно скомпилится в кеш в виде:

<?php

class aviaport_admin_meta_page extends bors_page
{

    function config_class() { return 'aviaport_admin_config'; }

    function class_file() { return ec('/var/www/admin.aviaport.ru/bors-site/classes/aviaport/admin/meta/page.yaml'); }
}

KRoN73 ★★★★★
() автор топика
Последнее исправление: KRoN73 (всего исправлений: 2)
Ответ на: комментарий от KRoN73

относительно свежие benchmarks разных форматов: http://konrness.com/php5/zend_config-benchmark-json-array-ini-xml-yaml/ yaml там в дауне по чтению только запись быстрая)) - эти тесты не мешает перепроверить, но меня yaml устраивает пока. Была статья - там yaml наоборот в первой тройке.

ini, array, php-configs самые быстрые

Быстро парсится, хорошо смотрится, а деревья в конфиге мне пока не нужны были.

Это да, если плоская структура - мне для вложенности и наглядности yaml самое то.

swwwfactory ★★
()
Ответ на: комментарий от KRoN73

А, вообще, использование глобальных конфигов оказалсь порочной практикой

держать локальные конфиги на каждом «хосте» и мержить с «distfiles»

у тебя config_class: aviaport_admin_config и aviaport_admin_meta_page в классе должны различаться или совпадать?

swwwfactory ★★
()
Ответ на: комментарий от xpahos
config.add_route('idea', 'ideas/{idea}', view='mypackage.views.idea_view')
config.add_route('user', 'users/{user}', view='mypackage.views.user_view')
config.add_route('tag', 'tags/{tags}', view='mypackage.views.tag_view')

пример из pyrimad документации. Вполне удобно.

С разморозкой, аргумент view в add_route депрекейтед c pyramid 1.1 Сейчас либо так

config.add_view(view='mypackage.views.idea_view', route_name='idea')
либо декоратором(что предпочтительней)
@view_config(route_name='idea')
def idea_view(request):
    return Response()

bismi
()
Ответ на: комментарий от swwwfactory

yaml там в дауне по чтению

Поскольку у меня чтение yaml одноразовое, для генерации php-классов, то пофиг :)

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от swwwfactory

у тебя config_class: aviaport_admin_config и aviaport_admin_meta_page в классе должны различаться или совпадать?

Это разные классы. Второй — вьюха определённого типа, первый — класс-конфигуратор объектов (прописывает для них единые права доступа, шаблоны, другие настройки).

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

ок

у тебя классы автоладом грузятся?

почему бы не использовать схему для роутинга такую: часть пути отрезаешь начало - «мапишь» на имя модуля (класса) и передаешь ему остальное как часть пути.

/a/b/c/x/y/z --> class a_b_c('x/y/z')

swwwfactory ★★
()
Ответ на: комментарий от KRoN73

Работаю в рамках компилятора, внутренний DSL-с )

vertexua ★★★★★
()
Ответ на: комментарий от swwwfactory

у тебя классы автоладом грузятся?

Да, естественно.

почему бы не использовать схему для роутинга такую: часть пути отрезаешь начало - «мапишь» на имя модуля (класса) и передаешь ему остальное как часть пути.

В случае автороутинга непонятно, до куда резать (можно, конечно, тупо с конца перебирать, пока не появится класс), в случае явно указанного роутинга такое и практикуется. Сейчас — так:
/a/b/c/(x/y/z) => a_b_c(1)

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от enep

а чего твой вариант не нравится

Когда больше двух параметров передаётся — начинаются лишние сущности. Да и так лишняя обвязка. Вызов функции, массив, regexp-группировки. Улучшение до варианта playframewok-like, в общем, косметическое, но работать будет приятнее :)

Плюс, как уже говорил, можно будет закешировать результат парсинга.

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

Правда, при этом подходе я теряю одну нынешнюю фишку — по умолчанию первая группировка считается автоматическим родителем объекта в навигации (в хлебных крошках). Т.е.:
(/forums/topics/)(\d+) => forum_topics_view(2)
в этом случае у forum_topics_view родителем в навигации автоматически будет /forums/topics/.

В принципе, если родитель является тупо родительским каталогом (львиная доля случаев, сейчас, если нет группировок, то так и считается), то проблема не возникает. А в более сложных случаях, часто родитель вообще по другому пути бывает, так что проблема лишних явных указаний родителей будет появляться весьма редко. Зато роутинг избавится от лишних сущностей.

KRoN73 ★★★★★
() автор топика

Если из django-вого выкинуть немного синтаксического мусора (которые тривиальный код с регекспом добавит обратно при чтении), получится вполне сносно:

articles/2003/$
  news.views.special_case_2003
articles/«year:\d{4}»/$
  news.views.year_archive
articles/«year:\d{4}»/«month:\d{2}»/$
  news.views.month_archive
articles/«year:\d{4}»/«month:\d{2}»/«day:\d{2}»/$
  news.views.article_detail

Не уверен, что можно сделать намного красивее без потерь.

DonkeyHot ★★★★★
()

У Yesod это отдельный файл routes (хотя можно и в коде делать) формата:

/blog/#BlogId HandlerR Get Post Delete
/static/ StaticR Static getStatic
/blog/year/#Int/month/#Int MonthR Get
/wiki/*Text WikiR

где #Тип обозначает автоматическое преобразование к типу с возможностью добаления своих (хотя динамическим языкам почти пофиг), *Тип обозначает массив типов, потом идёт имя хэнлера или модуля (subsite в терминах yesod), это имя является типом, который можно использовать для безопасного роутинга, генеринья ссылок, установки правил авторизации и т.д. в случае модуля указывается тип модуля и функция его получения, потом указываются запросы на которые идёт ответ.

Регекспов явно нет, но эту проверку можно делегировать функции. Можно делать «пересекающиеся» пути.

забыл дописать, что пути иерархические, т.е. можно делать

/a A
   /b B

qnikst ★★★★★
()
Последнее исправление: qnikst (всего исправлений: 1)
Ответ на: комментарий от qnikst

а у scotty, например, вобще

   get "/foo/bar/:hash" function

можно инлайнить всё напр, get «/foo/bar/:hash» $ \hash -> ..., причём с автовыводом типа и сообщением об ошибке, если тип распарсен неверно.

qnikst ★★★★★
()

Mojolicious:
$admins_only->route('/blog/edit/:post_id',post_id => qr/[a-zA-Z0-9]+/)->to('admin#editpost');
$admins_only->route('/blog/delete/:post_id',post_id => qr/[a-zA-Z0-9]+/)->to('admin#remove');
$admins_only->route('/blog/add')->via('post')->to('admin#doaddpost');
$admins_only->route('/blog/add')->to('admin#addpost');

Еще Mojolicious::Lite, Dancer и Sinatra посмотри.

iSage ★★★★
()

rails - способов записи маршрутов куча.

iBliss
()
Ответ на: комментарий от KRoN73

твою мысль понял

по ходу нужно пилить свой

enep ★★★★★
()

У Django роутинг просто ужасен:

А мне наоборот, понравился. Плюс можно писать красивее:

urlpatterns = patterns('',
	# ...
	include(r"^articles/", "news.urls")
)

# news/urls.py
urlpatterns = patterns('news.views',
	(r'^2003/$', 'special_case_2003'),
	
	(r'^(?P<year>\d{4})/$',								 'year_archive'),
	(r'^(?P<year>\d{4})/(?P<month>\d{2})/$',				'month_archive'),
	(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$', 'article_detail'),
)

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

Kalashnikov ★★★
()
Ответ на: комментарий от KRoN73

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

если сделать инклюду это будет просто

url('/regexp/', imported_func)

иногда удобней строками передать

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

trashymichael ★★★
()

В своем фреймверке использую такой формат (парсится как ini):

add = add
/:catpath*/:id = show :id:/^\d+$/
/:catpath+/ = list
/ = main
В данном случае руты встроены прямо в класс (класс узнается из таблицы секций БД), как метаданные. Если неизвестен класс (общий routes.ini), тогда так:
reg/:type? = Registration::show
captcha = Captcha::show
Передача управления на руты определенного класса (с отрезанием кусочка url):
profile => Profile

anonymous
()
Ответ на: комментарий от KRoN73

Да, естественно.

зачетно ))

В случае автороутинга непонятно, до куда резать

У тебя автолоад - значит есть список классов и модулей. Несложно найти нужный класс. А дальше дело класса, что делать с этим «остатком пути»

swwwfactory ★★
()

symfony/yaml

/thread

anonymous
()

Clojure noir

(defpage [:get "/"] [] "This is a get")
(defpage [:post "/"] [] "This is a post")
(defpage [:post "/upload"] {:keys [garden-file]})

st4l1k ★★
()
match "/:year(/:month(/:day))" => "info#about", :constraints => 
{ :year => /\d{4}/, :month => /\d{2}/, :day => /\d{2}/ }

ИМХО, это наоборот плюс. Я сразу же вижу, как выглядит путь, а затем, если необходимо, могу посмотреть формат каждого регэкспа. Без необходимости парсить взглядом регулярки и искать, какой же путь принимает какие параметры. Но писать дольше их, да.

drakmail ★★★★
()
Ответ на: комментарий от st4l1k

А как там указать, путь вида /2012/10/10, который состоит из 3-х GET параметров, первый из которых может содержать только цифры и иметь не более 4-х символов, а следующие два могут содержать только цифры и иметь не более 2-х символов длины?

drakmail ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.