ЁЯУЭ рдзрдбрд╛ 04 тАФ Validation рдЖрдгрд┐ mapping: form рдЪреА рддрдкрд╛рд╕рдгреА рдЖрдгрд┐ рд╕реНрд╡рдЪреНрдЫ рдЙрддреНрддрд░
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 04 ┬╖ рдорд╛рдЧреЗ: lesson-03-routes-integrations ┬╖ рдкреБрдвреЗ: lesson-05-stages-deployments
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбреЗ 01тАУ03, рдЖрдгрд┐ office рдЖрдд рдпреЗрддрд╛рдирд╛ рдЖрдгрд┐ рдмрд╛рд╣реЗрд░ рдЬрд╛рддрд╛рдирд╛ рдХрд░рддреЗ рддреА рджреЛрди рдХрд╛рдореЗ:
request validation (kitchen рд▓рд╛ call рдХрд░рдгреНрдпрд╛рдЖрдзреАрдЪ рдЪреБрдХреАрдЪреА body 400 рдиреЗ рдирд╛рдХрд╛рд░рд▓реА рдЬрд╛рддреЗ)
рдЖрдгрд┐ response mapping (office kitchen рдЪреНрдпрд╛ рдЙрддреНрддрд░рд╛рддреВрди phone number рдХрд╛рдвреВрди рдЯрд╛рдХрддреЗ).
apigw/demo.py рдордзрд▓реЗ validate() рджреЛрдиреНрд╣реА рджрд╛рдЦрд╡рддреЗ.
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
рдПрдХ рд╢рд┐рдХреНрд╖рд┐рдХрд╛ clerk рдХрдбреЗ рдПрдХ marks form рджреЗрддреЗ. Form рдордзрд▓реЗ рддреАрди рд░рдХрд╛рдиреЗ рднрд░рд▓реЗрд▓реЗ рдЕрд╕рд╛рдпрд▓рд╛рдЪ рд╣рд╡реЗрдд: рд╡рд┐рджреНрдпрд╛рд░реНрдереА, рд╡рд┐рд╖рдп, mark. рдЖрдгрд┐ mark рд╣рд╛ рдЖрдХрдбрд╛ рдЕрд╕рд╛рдпрд▓рд╛ рд╣рд╡рд╛.
Clerk form desk рд╡рд░рдЪ тЬНя╕П рддрдкрд╛рд╕рддреЛ. рдПрдЦрд╛рджрд╛ рд░рдХрд╛рдирд╛ рд░рд┐рдХрд╛рдорд╛ рдЖрд╣реЗ? "рдХреГрдкрдпрд╛ mark рднрд░рд╛." Mark рдЕрдХреНрд╖рд░рд╛рдВрдд "рдирд╡реНрд╡рдж" рдЕрд╕рд╛ рд▓рд┐рд╣рд┐рд▓рд╛ рдЖрд╣реЗ? "Mark рдЖрдХрдбреНрдпрд╛рддрдЪ рд╣рд╡рд╛." Form рд╢рд┐рдХреНрд╖рд┐рдХреЗрдХрдбреЗ рдкрд░рдд рдЬрд╛рддреЛ тАФ kitchen рд▓рд╛ рддреЛ рдХрдзреА рджрд┐рд╕рддрдЪ рдирд╛рд╣реА, рдЖрдгрд┐ рддреНрдпрд╛рд╡рд░ kitchen рдЪрд╛ рд╡реЗрд│рд╣реА рд╡рд╛рдпрд╛ рдЬрд╛рдд рдирд╛рд╣реА.
рдмрд╛рд╣реЗрд░ рдЬрд╛рддрд╛рдирд╛, kitchen рд╡рд┐рджреНрдпрд╛рд░реНрдереНрдпрд╛рдЪреЗ card рдкрд░рдд рдкрд╛рдард╡рддреЗ рдЬреНрдпрд╛рд╡рд░ рдЕрдЬреВрдирд╣реА phone number рдЖрд╣реЗ. Card рджреЗрдгреНрдпрд╛рдЖрдзреА clerk рддреЛ phone number рдЭрд╛рдХрддреЛ. Kitchen рд╕реЛрдкреЗ рд░рд╛рд╣рддреЗ; visitor рд▓рд╛ рдлрдХреНрдд рддреНрдпрд╛рдиреЗ рдкрд╛рд╣рд╛рдпрд▓рд╛ рд╣рд╡реЗ рддреЗрд╡рдвреЗрдЪ рджрд┐рд╕рддреЗ.
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
t["ЁЯСйтАНЁЯПл POST /marks"] --> chk{"ЁЯУЭ form check<br/>required: student, subject, mark<br/>mark must be int"}
chk -->|"mark missing"| e1["тЭМ 400 missing required field 'mark'"]
chk -->|"mark = 'ninety'"| e2["тЭМ 400 'mark' must be int"]
chk -->|"ok"| k["ЁЯН│ kitchen saves it тЖТ 201"]
k2["ЁЯН│ GET /students/7<br/>id ┬╖ name ┬╖ class ┬╖ phone"] --> map["тЬВя╕П response mapping<br/>remove phone"] --> out["ЁЯУД id ┬╖ name ┬╖ class"]
ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/apigateway/lesson-diagrams.html#l04
тЭУ рдХрд╛рдп
- Request validation (REST APIs) тАФ request validator body рд▓рд╛ рдПрдХрд╛ model (JSON
Schema, draft 4) рд╡рд┐рд░реБрджреНрдз рддрдкрд╛рд╕рддреЛ, рдЖрдгрд┐/рдХрд┐рдВрд╡рд╛ рдЖрд╡рд╢реНрдпрдХ query strings рдЖрдгрд┐ headers рдЖрд╣реЗрдд рдХрд╛ рддреЗ
рддрдкрд╛рд╕рддреЛ. рдЕрдкрдпрд╢ рдЖрд▓реНрдпрд╛рд╕
{"message": "Invalid request body"}рд╕рд╣400рдкрд░рдд рдпреЗрддреЛ рдЖрдгрд┐ integration рд▓рд╛ call рдХреЗрд▓реЗ рдЬрд╛рдд рдирд╛рд╣реА. - Model тАФ body рд╕рд╛рдареАрдЪреЗ JSON Schema: рдЖрд╡рд╢реНрдпрдХ fields, types, рд▓рд╛рдВрдмреА, patterns.
- Mapping templates (REST APIs, custom / non-proxy integrations) тАФ VTL рдордзрд▓реЗ рдЫреЛрдЯреЗ programs, рдЬреЗ back end рд▓рд╛ рджрд┐рд╕рдгреНрдпрд╛рдЖрдзреА request, рдХрд┐рдВрд╡рд╛ caller рд▓рд╛ рджрд┐рд╕рдгреНрдпрд╛рдЖрдзреА response рдирд╡реНрдпрд╛рдиреЗ рдорд╛рдВрдбрддрд╛рдд. Proxy integration рдордзреНрдпреЗ templates рдирд╕рддрд╛рдд: request рдЬрд╢реАрдЪреНрдпрд╛ рддрд╢реА рдЬрд╛рддреЗ рдЖрдгрд┐ back end рдкреВрд░реНрдг response рдмрдирд╡рддреЛ.
- HTTP APIs рдордзреНрдпреЗ request validation рдирд╛рд╣реА рдЖрдгрд┐ VTL рдирд╛рд╣реА; рддреЗ рд╕реЛрдкреЗ parameter mapping рджреЗрддрд╛рдд (headers, query strings рдЖрдгрд┐ paths рдЬреЛрдбрдгреЗ, рдмрджрд▓рдгреЗ рдХрд┐рдВрд╡рд╛ рдХрд╛рдврдгреЗ).
- Gateway responses тАФ office рдЪреА рд╕реНрд╡рддрдГрдЪреА error рдЙрддреНрддрд░реЗ (
BAD_REQUEST_BODY,UNAUTHORIZED,THROTTLEDтАж). рддреНрдпрд╛рдВрдЪреА body рдЖрдгрд┐ headers рддреБрдореНрд╣реА рдмрджрд▓реВ рд╢рдХрддрд╛, рдЙрджрд╛.$context.error.validationErrorStringрд╕рдорд╛рд╡рд┐рд╖реНрдЯ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА.
ЁЯдФ рдХрд╛
рдХрд╛рд░рдг kitchen рдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪрд▓реЗрд▓реНрдпрд╛ рдЪреБрдХреАрдЪреНрдпрд╛ form рдЪреА рдХрд┐рдВрдордд рдореНрд╣рдгрдЬреЗ рдПрдХ Lambda run, рдХрджрд╛рдЪрд┐рдд рдПрдХ
database call, рдЖрдгрд┐ caller рд╕рд╛рдареА рдЧреЛрдВрдзрд│рд╛рдд рдЯрд╛рдХрдгрд╛рд░рд╛ 500. рджрд╛рд░рд╛рд╡рд░рдЪ рдЖрдХрд╛рд░ рддрдкрд╛рд╕рд▓рд╛ рдХреА рд╕реНрдкрд╖реНрдЯ 400
рдорд┐рд│рддреЛ, back end рдХрдЪрд▒реНрдпрд╛рдкрд╛рд╕реВрди рд╕реБрд░рдХреНрд╖рд┐рдд рд░рд╛рд╣рддреЛ, рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ client рд╕рд╛рдареА рдПрдХрдЪ рд▓рд┐рдЦрд┐рдд рдХрд░рд╛рд░ (model)
рд░рд╛рд╣рддреЛ. Response mapping рдореБрд│реЗ рдЦрд╛рдЬрдЧреА fields тАФ phone numbers, рдЖрддрд▓реЗ ids тАФ рдлрдХреНрдд back end рдиреЗ рдкрд░рдд
рджрд┐рд▓реЗ рдореНрд╣рдгреВрди рдмрд╛рд╣реЗрд░ рдЬрд╛рдд рдирд╛рд╣реАрдд.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
apigw/gateway.py рдордзрд▓реЗ validate(schema, body) рд╣реЗ рдПрдХ рдЫреЛрдЯреЗрд╕реЗ
JSON-schema рдЖрд╣реЗ: required fields рдЖрдгрд┐ types. office() рдордзрд▓реНрдпрд╛ POST /marks route рд▓рд╛
schema={"required": ["student", "subject", "mark"], "types": {"mark": int}} рдЖрд╣реЗ.
Gateway._handle() рдордзреНрдпреЗ validation integration рдЪреНрдпрд╛ рдЖрдзреА рдЪрд╛рд▓рддреЗ, рддреНрдпрд╛рдореБрд│реЗ рдЪреБрдХреАрдЪреА body
рдХрдзреАрдЪ add_mark() рдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪрдд рдирд╛рд╣реА. GET /students/{id} route рд▓рд╛ mapping=hide_phone
рдЖрд╣реЗ, рдЬреЗ kitchen рдЪреНрдпрд╛ рдЙрддреНрддрд░рд╛рд╡рд░ рдмрд╛рд╣реЗрд░ рдЬрд╛рддрд╛рдирд╛ рд▓рд╛рдЧреВ рд╣реЛрддреЗ тАФ custom integration рд╡рд░рдЪреНрдпрд╛ response
mapping template рдЪреА model рдордзрд▓реА рдЖрд╡реГрддреНрддреА. Students kitchen рд▓рд╛ рдЦрд░реЛрдЦрд░ рдХрд┐рддреА рд╡реЗрд│рд╛ call рдХреЗрд▓реЗ рддреЗ
calls["n"] рдореЛрдЬрддреЗ.
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
python3 apigw/demo.py validate
python3 - <<'EOF'
import sys; sys.path.insert(0, "apigw"); from demo import office, token, calls
from gateway import validate
gw, c = office()
H = {"Authorization": "Bearer " + token(c, scope="marks:write")}
for body in (["7", "maths", 91], {"student": "7", "subject": "maths", "mark": 91.5}, None):
print(gw.handle("POST", "/marks", H, body)[0::2])
schema = {"required": ["student", "subject", "mark"], "types": {"mark": int}}
print(validate(schema, {"student": "7", "subject": "maths"}))
calls["n"] = 0; gw.handle("GET", "/students/9"); print("kitchen calls:", calls["n"], "┬╖ answer:", gw.handle("GET", "/students/9")[2])
EOF
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
validate рджреЛрди рдирдХрд╛рд░ рдЖрдгрд┐ рдПрдХ рдпрд╢ print рдХрд░рддреЗ:
POST /marks тЖТ 400 {'message': 'Invalid request body', 'why': "missing required field 'mark'"},
POST /marks тЖТ 400 {'message': 'Invalid request body', 'why': "'mark' must be int"},
POST /marks тЖТ 201 {'saved': {'student': '7', 'subject': 'maths', 'mark': 91}, 'table': 'marks-prod'}.
рдордЧ mapping: kitchen: {'id': '7', 'name': 'Aishwarya', 'class': '8B', 'phone': '98xxxxxx12'}
рдЖрдгрд┐ GET /students/7 тЖТ 200 {'id': '7', 'name': 'Aishwarya', 'class': '8B'} тАФ phone рдирд╛рд╣реА.
рддреБрдордЪрд╛ snippet list рд╕рд╛рдареА
(400, {'message': 'Invalid request body', 'why': 'body must be a JSON object'}),
91.5 рд╕рд╛рдареА (400, {'message': 'Invalid request body', 'why': "'mark' must be int"}),
body рдирд╕рддрд╛рдирд╛ рдкреБрдиреНрд╣рд╛ body must be a JSON object, рдордЧ
missing required field 'mark', рдЖрдгрд┐
kitchen calls: 1 ┬╖ answer: {'id': '9', 'name': 'Katrina', 'class': '9A'} print рдХрд░рддреЛ.
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
рдЪреБрдХреАрдЪрд╛ form рдХрд╛рд░рдгрд╛рд╕рд╣ desk рд╡рд░рдЪ рдирд╛рдХрд╛рд░рд▓рд╛ рдЬрд╛рддреЛ, рддреНрдпрд╛рд╕рд╛рдареА kitchen рд▓рд╛ call рд╣реЛрдд рдирд╛рд╣реА, рдЖрдгрд┐ kitchen рди рдмрджрд▓рддрд╛ рдмрд╛рд╣реЗрд░ рдЬрд╛рддрд╛рдирд╛ рдЙрддреНрддрд░ рд╕реНрд╡рдЪреНрдЫ рдХрд░рддрд╛ рдпреЗрддреЗ.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
- body рд╕рд╛рдареА validation рдЪрд╛рд▓реВ рдХреЗрд▓реЗ рдкрдг model рд╕реИрд▓ рдЖрд╣реЗ (
requiredрдирд╛рд╣реА) тАФ рд╕рдЧрд│реЗ рдХрд╛рд╣реА рдкрд╛рд╕ рд╣реЛрддреЗ - business рдирд┐рдпрдо ("рдпрд╛ рд╡рд┐рд╖рдпрд╛рд╕рд╛рдареА mark рдЬрд╛рд╕реНрддреАрдд рдЬрд╛рд╕реНрдд 100") VTL рдордзреНрдпреЗ рдЯрд╛рдХрдгреЗ тАФ templates рдЖрдХрд╛рд░рд╛рд╕рд╛рдареА рдареЗрд╡рд╛, logic code рдордзреНрдпреЗ
- proxy integration рд╡рд░ mapping templates рдЪреА рдЕрдкреЗрдХреНрд╖рд╛ рдХрд░рдгреЗ тАФ рддрд┐рдереЗ рддреЗ рд▓рд╛рдЧреВ рд╣реЛрдд рдирд╛рд╣реАрдд
- response mapping рдордзреНрдпреЗ field рд▓рдкрд╡рдгреЗ рдЖрдгрд┐ рддрд░реАрд╣реА back end рдЪреЗ рдкреВрд░реНрдг рдЙрддреНрддрд░ log рдХрд░рдгреЗ
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд
On a real account тАФ REST API рд╕рд╛рдареА OpenAPI рдордзрд▓реЗ model рдЖрдгрд┐ validator:
x-amazon-apigateway-request-validators:
body-only: { validateRequestBody: true, validateRequestParameters: false }
paths:
/marks:
post:
x-amazon-apigateway-request-validator: body-only
requestBody:
content:
application/json:
schema:
type: object
required: [student, subject, mark]
properties:
student: { type: string }
subject: { type: string }
mark: { type: integer, minimum: 0, maximum: 100 }
Phone рдХрд╛рдвреВрди рдЯрд╛рдХрдгрд╛рд░реЗ, custom Lambda integration рд╡рд░рдЪреЗ response mapping template (VTL):
#set($s = $input.path('$'))
{ "id": "$s.id", "name": "$s.name", "class": "$s.class" }
ЁЯПн Production рдордзреНрдпреЗ рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: request validation partners рдирд╛ рд╕реНрдкрд╖реНрдЯ
400рджреЗрддреЗ рдЖрдгрд┐ database рдкрд╛рд╕реВрди рдХрдЪрд░рд╛ рджреВрд░ рдареЗрд╡рддреЗ; fields рдмрд╛рд╣реЗрд░ рдЬрд╛рдгреНрдпрд╛рд╡рд┐рд░реБрджреНрдз response mapping рд╣реА рд╢реЗрд╡рдЯрдЪреА рдлрд│реА рдЖрд╣реЗ. рджреЛрдиреНрд╣реАaws apigateway test-invoke-methodрдиреЗ test рдХрд░рд╛.
тПня╕П рдкреБрдвреЗ
Desk рдЪрд╛рд▓рддреЛ. рдЦрд░рд╛ рд╢рд╛рд│реЗрдЪрд╛ рджрд┐рд╡рд╕ рди рдмрд┐рдШрдбрд╡рддрд╛ рддреЛ рдХрд╕рд╛ рдмрджрд▓рд╛рдпрдЪрд╛? Stages рдЖрдгрд┐ deployments тАФ рд░рдВрдЧреАрдд рддрд╛рд▓реАрдо рдЖрдгрд┐ рдЦрд░рд╛ рдкреНрд░рдпреЛрдЧ.
git checkout lesson-05-stages-deployments