ESC

Type to search...

Form Data

EVOID never touches HTTP parsing — form data extraction happens in your adapter. The runtime sees only the dict your adapter places in intent.metadata["form"].

Parsing Form Data in Your Adapter

For URL-encoded forms, parse the body in your adapter:

from urllib.parse import parse_qs
from evoid.core import Intent, Level


def intent_from_form_request(method: str, path: str, body: bytes,
                             headers: dict) -> Intent:
    content_type = headers.get("content-type", "")
    form_data = {}

    if "application/x-www-form-urlencoded" in content_type:
        parsed = parse_qs(body.decode("utf-8"))
        form_data = {k: v[0] if len(v) == 1 else v for k, v in parsed.items()}

    return Intent(
        name=f"{method.lower()}:{path}",
        level=Level.STANDARD,
        metadata={
            "method": method,
            "path": path,
            "form": form_data,
            "headers": headers,
        },
    )

Multipart Form Parsing

For multipart forms (file uploads, complex data), use python-multipart:

import json
from evoid.core import Intent, Level


async def intent_from_multipart(request) -> Intent:
    from starlette.datastructures import UploadFile

    form = await request.form()
    form_data = {}
    files = {}

    for key in form:
        value = form[key]
        if isinstance(value, UploadFile):
            files[key] = {
                "filename": value.filename,
                "content_type": value.content_type,
                "size": value.size,
            }
        else:
            form_data[key] = value

    return Intent(
        name=f"{request.method}:{request.url.path}",
        level=Level.STANDARD,
        metadata={
            "method": request.method,
            "path": request.url.path,
            "form": form_data,
            "files": files,
            "headers": dict(request.headers),
        },
    )

Starlette Integration

from starlette.requests import Request
from starlette.responses import JSONResponse
from evoid.core import execute


async def handle(request: Request) -> JSONResponse:
    content_type = request.headers.get("content-type", "")

    if "application/x-www-form-urlencoded" in content_type:
        form = await request.form()
        form_data = dict(form)
    elif "application/json" in content_type:
        form_data = await request.json()
    else:
        form_data = {}

    from evoid.core import Intent, Level
    intent = Intent(
        name=f"{request.method}:{request.url.path}",
        level=Level.STANDARD,
        metadata={"form": form_data, "headers": dict(request.headers)},
    )

    result = await execute(intent)
    if result.success:
        return JSONResponse(result.value)
    return JSONResponse({"error": str(result.error)}, status_code=500)

Accessing Form Data in Processors

from evoid.core import Context


async def validate_registration(ctx: Context) -> dict:
    form = ctx.metadata.get("form", {})

    required = ["username", "email", "password"]
    missing = [f for f in required if f not in form]

    if missing:
        raise ValueError(f"Missing fields: {', '.join(missing)}")

    return {"validated": True, "fields": list(form.keys())}


async def create_user(ctx: Context) -> dict:
    form = ctx.metadata.get("form", {})
    return {"status": "created", "username": form["username"]}

@route Style

from evoid.web.route import Service, post, before

app = Service("api")

@app.post("/register")
async def register(username: str, email: str, password: str) -> dict:
    return {"status": "created", "username": username}

before("POST:/register", "validate_registration")

@controller Style

from evoid.web.controller import Service, Controller, POST

app = Service("api")

@Controller("/users")
class UserController:
    @POST("/register")
    async def register(self, username: str, email: str, password: str) -> dict:
        return {"status": "created", "username": username}

Native IOP Style

from evoid import Intent, Level, add_intent

REGISTER = Intent(
    name="post:register",
    level=Level.STANDARD,
)


async def handle_register(intent: Intent) -> dict:
    form = intent.metadata.get("form", {})
    username = form.get("username")

    if not username:
        raise ValueError("Username is required")

    return {"status": "created", "username": username}


add_intent(REGISTER, handle_register)