Усовершенствования маршрутизации для GO 1.22, о которых вы должны знать

Усовершенствования маршрутизации для GO 1.22, о которых вы должны знать

8 июня 2025 г.

GO 1.22 приносит два улучшения вnet/httpМаршрутизатор пакета: сопоставление методов и подстановочные знаки. Эти функции позволяют выражать общие маршруты в виде шаблонов вместо кода GO. Хотя они просты в объяснении и использовании, было проблемой придумать правильные правила для выбора победного шаблона, когда несколько соответствуют запросу.

Мы внесли эти изменения в рамках наших постоянных усилий, чтобы сделать отличный язык для построения производственных систем. Мы изучили много сторонних веб-фреймворков, извлекли то, что, как мы чувствовали, были самыми используемыми функциями, и интегрировали их вnet/httpПолем Затем мы подтвердили наш выбор и улучшили наш дизайн, сотрудничая с сообществом вДискуссия GitHubиПроблема предложенияПолем Добавление этих функций в стандартную библиотеку означает меньше зависимости для многих проектов. Но сторонние веб-фреймворки остаются отличным выбором для текущих пользователей или программ с расширенными потребностями маршрутизации.

Улучшения

Новые функции маршрутизации почти исключительно влияют на строку шаблона, передаваемую к двумnet/http.ServeMuxметодыHandleиHandleFuncи соответствующие функции верхнего уровняhttp.Handleиhttp.HandleFuncПолем Единственные изменения API - два новых метода наnet/http.RequestДля работы с матчами с подстановочными знаками.

Мы проиллюстрируем изменения с гипотетическим сервером блога, в котором каждый пост имеет целочисленный идентификатор. Запрос, какGET /posts/234Получает пост с ID 234. Перед GO 1.22, код для обработки этих запросов начнется с такой строки, как это:

http.HandleFunc("/posts/", handlePost)

Тропительные снопки маршрутов все начинаются/posts/вhandlePostФункция, которая должна была бы проверить, что метод HTTP был GET, извлечь идентификатор и получить сообщение. Поскольку проверка метода не является строго необходимой для удовлетворения запроса, было бы естественной ошибкой опустить его. Это будет означать, что запрос такойDELETE /posts/234Принесет пост, который, по крайней мере, удивительно.

В Go 1.22 существующий код будет продолжать работать, или вместо этого вы можете написать это:

http.HandleFunc("GET /posts/{id}", handlePost2)

Этот шаблон соответствует запросу GET, путь которого начинается «/posts/» и имеет два сегмента. (В качестве особого случая, получите также совпадает с головой; все остальные методы точно соответствуют.)handlePost2Функции больше не нужно проверять метод, и извлечение строки идентификатора может быть записано с помощью новогоPathValueМетод наRequest:

idString := req.PathValue("id")

Остальная частьhandlePost2будет вести себя какhandlePost, преобразование идентификатора строки в целое число и получение поста.

Запросы, какDELETE /posts/234не пройдет, если не будет зарегистрирована другая схема соответствия. В соответствии сHTTP Semantics, аnet/httpсервер ответит на такой запрос с помощью405 Method Not Allowedошибка, которая перечисляет доступные методы вAllowзаголовок.

Подстановка может соответствовать целую сегменту, как{id}в примере выше, или если он заканчивается...он может соответствовать всем оставшимся сегментам пути, как в шаблоне/files/{pathname...}Полем

Есть последний бит синтаксиса. Как мы показали выше, узоры, заканчивающиеся в черниле, как/posts/, сопоставьте все пути, начиная с этой строки. Чтобы соответствовать только пути с затяжкой чертой, вы можете написать/posts/{$}Полем Это будет соответствовать/posts/но нет/postsили/posts/234Полем

И есть последний кусочек API:net/http.RequestимеетSetPathValueМетод, чтобы маршрутизаторы вне стандартной библиотеки могли предоставить результаты своего собственного анализа пути доступным черезRequest.PathValueПолем

Приоритет

Каждый маршрутизатор HTTP должен иметь дело с перекрывающимися шаблонами, как/posts/{id}и/posts/latestПолем Оба эти шаблона соответствуют пути «Посты/Последние», но больше всего можно обслуживать запрос. Какой шаблон имеет приоритет?

Некоторые маршрутизаторы запрещают совпадения; Другие используют шаблон, который был зарегистрирован последним. GO всегда разрешал совпадения и выбрал более длинную шаблон независимо от заказа регистрации. Сохранение независимости от порядка было важно для нас (и необходимо для обратной совместимости), но нам нужно было лучшее правило, чем «самые длинные победы». Это правило выберет/posts/latestнад/posts/{id}, но выбрал бы/posts/{identifier}над обоими. Это кажется неправильным: имя подстановочного знака не должно иметь значения. Это похоже/posts/latestВсегда должен выиграть этот конкурс, потому что он соответствует одному пути вместо многих.

Наше стремление к хорошему правилу приоритета привело нас к рассмотрению многих свойств моделей. Например, мы рассмотрели предпочтение шаблона с самым длинным буквальным (без WildCard) префиксом. Это выбрало бы/posts/latestнад/posts/ {id}Полем Но это не будет различать/users/{u}/posts/latestи/users/{u}/posts/{id}, и кажется, что первый должен иметь приоритет.

В конце концов мы выбрали правило, основанное на том, что означают закономерности, а не то, как они выглядят. Каждый действительный шаблон соответствует набору запросов. Например,/posts/latestсоответствует запросам с пути/posts/latest, пока/posts/{id}соответствует запросам с любым двухсегментным путем, первым сегментом которого являются «сообщения». Мы говорим, что один шаблонболее конкретныйчем другое, если это соответствует строгому подмножеству запросов. Шаблон/posts/latestболее конкретно, чем/posts/{id}Потому что последний соответствует каждому запросу, который делает первый, и многое другое.

Правило приоритета простое: самый конкретный шаблон выигрывает. Это правило соответствует нашей интуиции, чтоposts/latestsдолжен быть предпочтительнымposts/{id}, и/users/{u}/posts/latestдолжен быть предпочтительным/users/{u}/posts/{id}Полем Это также имеет смысл для методов. Например,GET /posts/{id}имеет приоритет/posts/{id}Потому что первые только совпадают с запросами Get и Head, в то время как вторые соответствуют запросам с любым методом.

Правило «самые конкретные победы» обобщает оригинальное правило «самые длинные победы» для частей пути оригинальных моделей, тех, у кого нет подстановочных знаков или{$}Полем Такие шаблоны перекрываются только тогда, когда один является префиксом другого, и чем дольше тем более конкретно.

Что если два шаблона перекрываются, но не более конкретно? Например,/posts/{id}и/{resource}/latestоба совпадают/posts/latestПолем Нет очевидного ответа, который имеет приоритет, поэтому мы рассматриваем эти закономерности конфликтует друг с другом. Регистрация их обоих (в любом порядке!) Паникует.

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

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

Определение приоритета языками вместо выражений позволяет легко утверждать и понимать. Но есть недостаток в наличии правила, основанного на потенциально бесконечных наборах: неясно, как его реализовать. Оказывается, мы можем определить, конфликтуют ли два шаблона, проходя их сегмент по сегменту. Грубо говоря, если у одного шаблона есть буквальный сегмент, где у другого есть подстановочный знак, это более конкретно; Но если литералы совпадают с подстановочными знаками в обоих направлениях, конфликт моделей.

Поскольку новые шаблоны зарегистрированы наServeMux, он проверяет конфликты с ранее зарегистрированными шаблонами. Но проверка каждой пары узоров займет квадратичное время. Мы используем индекс, чтобы пропустить шаблоны, которые не могут конфликтовать с новым шаблоном; На практике это работает довольно хорошо. В любом случае, эта проверка происходит, когда шаблоны зарегистрированы, обычно при запуске сервера. Время соответствия входящих запросов в GO 1.22 не сильно изменилось из предыдущих версий.

Совместимость

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

Для получения более подробной информации об этих улучшениях маршрутизации см. Вnet/http.ServeMuxдокументация.


Джонатан Амстердам, от имени команды GO

ФотоВики МаклейннаНеспособный

Эта статья доступна наБлог GOПод CC по лицензии 4,0.


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE