ДокументацияФормы и данные
Действия
@Action превращает функцию Python в конечную точку POST. В отличие от стандартного обработчика API, он спроектирован как типизированный мост связи: Nexy автоматически генерирует клиент TypeScript, проверяет входные данные и обрабатывает ответ.
Прежде чем начать: проект, созданный с помощью Быстрого старта. Действия находятся рядом со страницами, в src/actions/.
Client (UI) ─── 1. Call (Typed Input) ───► [ Nexy Bridge ]
│
▼
2. Validate & Execute (Python)
│
▼
Client (UI) ◄─── 5. Consume (Typed Result) ◄── 4. Serialize ◄── 3. ResultЦикл действий
1. Определить
Действие — это обычная функция Python, отмеченная @Action. Nexy использует свою подпись для определения контракта API.
from pydantic import BaseModelfrom nexy.decorators import Action, revalidate class User(BaseModel): name: str age: int = 0 @Actionasync def create_user(name: str, age: int = 0) -> User: revalidate() return User(name=name, age=age)Параметры являются входными данными, проверенными Pydantic. Параметр с именем request или response вводится и не является частью входных данных.
2. Подтвердить
При вызове действия Nexy проверяет введенные данные на соответствие подсказкам типа функции. Если поле отсутствует или имеет неверный тип, действие не выполняется и возвращается 422 Unprocessable Entity.
3. Выполнить
Логика работает на сервере. Поскольку это стандартная функция Python, вы можете использовать любую библиотеку, обращаться к базе данных или вызывать другие службы. Вы также можете запустить обновление страницы, используя revalidate().
4. Сериализация
Возвращаемое значение автоматически сериализуется в JSON. Модели Pydantic обрабатываются изначально, а стандартные типы Python сопоставляются с их эквивалентами JSON.
5. Потребляйте
Интерфейс получает результат с полной безопасностью типов. Nexy записывает __nexy__/src/actions.ts из ваших подписей, а @nexy/actions разрешает его.
import { useAction } from "@nexy/client";import { create_user } from "@nexy/actions"; export default function NewUser() { const { run, pending, error, data } = useAction(create_user); return ( <button disabled={pending} onClick={() => run({ name: "Ada" })}> {error ? error.message : data ? `Created ${data.name}` : "Create"} </button> );}Компонент представляет собой остров представления, возвращаемого контроллером:
import { useAction } from "@nexy/client";import { create_user } from "@nexy/actions"; export function NewUser() { const { run, pending, error, data } = useAction(create_user); return ( <button disabled={pending} onClick={() => run({ name: "Ada" })}> {error ? error.message : data ? `Created ${data.name}` : "Create"} </button> );}from "@/components/NewUser.tsx" import NewUser<NewUser directive="client:load" />Без хука действие — это просто функция async: const user = await create_user({ name: "Ada" }).
| Рамочная основа | Что возвращает useAction |
|---|---|
| React, Preact | { pending, data, error, run, reset } |
| Solid | то же самое, с pending(), data() и error() в качестве аксессоров |
| Vue | то же самое, с pending, data и error в качестве ссылок |
| Svelte | магазин: $save.pending, $save.data, $save.error, плюс save.run(input) и save.reset() |
useAction(action, { onSuccess, onError }) также принимает два обратных вызова. run никогда не выдает: неудача возникает в error и run разрешается с помощью undefined.
Типы взяты из Python: str — это string, int и float — это number, list[T] — это T[], модель Pydantic или Enum — это именованный тип, а поле со значением по умолчанию является необязательным во входных данных. Тип, который Pydantic не может описать, становится unknown.
Ошибки и неудачи
Действие может потерпеть неудачу на разных этапах. Вы должны различать их в своем пользовательском интерфейсе:
- Ошибки проверки (422): введенные данные не соответствуют подписи Python.
error.fieldsсопоставляет каждое недействительное поле с его сообщением. - Бизнес-ошибки (
HTTPException): ваш код отклонил вызов, например403 Forbiddenили409 Conflict. - Ошибки сервера (500): в вашем коде Python произошло необработанное исключение.
- Сетевые ошибки: запрос так и не дошёл до сервера, например таймаут или потеря соединения.
- Ошибки авторизации (401, 403): ваша собственная проверка отклонила звонящего. Nexy не решает это за вас.
Описанная ниже проверка источника представляет собой защиту от CSRF, а не авторизацию: она говорит, откуда поступил вызов, но не указывает, кому разрешено его совершать.
Обновить страницу после действия
Действие сообщает, какие страницы его изменения устарели, как это делает Next.js с revalidatePath:
revalidate() # the page that is openrevalidate("/users") # that page, if it is the one open
Ответ содержит пути в заголовке x-nexy-revalidate. Затем клиент повторно извлекает текущую страницу через маршрутизатор или перезагружает ее, когда клиентский маршрутизатор отсутствует.
Формы без JavaScript
Действие также принимает стандартную HTML-форму, поэтому страница работает до загрузки скрипта:
<form method="post" action="{{ __actions_path['create_user'] }}"> <input name="name"> <button type="submit">Create</button></form>
В шаблоне .nexy __actions_path["name"] — это путь, __actions["name"] — функция, и функция также находится в области видимости под своим собственным именем.
В случае успеха посетитель возвращается на страницу, на которой находилась форма, со статусом 303. Чтобы ответить иначе, верните объект Response (например, RedirectResponse("/done", status_code=303)).
Форма, отправленная как application/x-www-form-urlencoded, что браузер делает по умолчанию, не требует дополнительного пакета. Добавьте enctype="multipart/form-data" и вызову потребуется python-multipart: без него действие отвечает на 500 сообщением с именем команды uv add python-multipart.
Действие считывает поля, а не файлы: ввод файла достигает его в виде строки. Чтобы получить загрузку, пропишите маршрут в src/routes/api/ с параметром File или UploadFile.
Безопасность
Неизвестность пути не является авторизацией
Путь действия — /_nx_, за которым следует хеш модуля функции, его имя и секрет проекта. Он устанавливается NEXY_ACTION_SECRET или сохраняется в __nexy__/action.key, который nx build создает, чтобы клиентский пакет и nx start согласились: сохранить этот файл вместе со сборкой. Путь скрывает конечную точку от случайного обнаружения, и сгенерированный путь не является механизмом авторизации.
Любой, кто знает путь, может вызвать действие. Вы должны проверить личность пользователя и его разрешения внутри действия:
@Actionasync def delete_user(user_id: str, request: Request): if not request.state.user.is_admin: raise HTTPException(403, "Admin rights required") # ...
Защита происхождения
Чтобы предотвратить подделку межсайтовых запросов (CSRF), Nexy отклоняет запросы, Origin которых является другим сайтом или которые браузер помечает Sec-Fetch-Site: cross-site знаком 403. Перечислите сайты, которые могут вызывать ваши действия в NEXY_ALLOWED_ORIGINS. Звонки без Origin (например, curl) принимаются.
Ошибки импорта
При запуске Nexy импортирует каждый модуль src/ для поиска действий. Сообщается о модуле, который не удалось импортировать, а остальные все еще загружаются:
x src.actions.billing: src/actions/billing.py:3 ModuleNotFoundError: No module named 'stripe'
Следующие шаги
- API: явные обработчики
- Тестирование: как протестировать действие
- Конфигурация: промежуточное программное обеспечение и параметры аутентификации.