ДокументацияФормы и данные

Действия

@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.

src/actions/user.py
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 разрешает его.

src/маршруты/users.tsx
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>  );}

Компонент представляет собой остров представления, возвращаемого контроллером:

источник/компоненты/NewUser.tsx
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>  );}
src/apps/users/user_view.nexy
---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.

Ошибки и неудачи

Действие может потерпеть неудачу на разных этапах. Вы должны различать их в своем пользовательском интерфейсе:

  1. Ошибки проверки (422): введенные данные не соответствуют подписи Python. error.fields сопоставляет каждое недействительное поле с его сообщением.
  2. Бизнес-ошибки (HTTPException): ваш код отклонил вызов, например 403 Forbidden или 409 Conflict.
  3. Ошибки сервера (500): в вашем коде Python произошло необработанное исключение.
  4. Сетевые ошибки: запрос так и не дошёл до сервера, например таймаут или потеря соединения.
  5. Ошибки авторизации (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: явные обработчики
  • Тестирование: как протестировать действие
  • Конфигурация: промежуточное программное обеспечение и параметры аутентификации.

Изменить на GitHub

Enter открывает результат, Esc закрывает.