HTTP 구성요소(Component)

ahttp_client 의 구성요소는 Query, Header, Path, 그리고 Body가 있으며,HTTP 요청을 처리하는 과정에서 사용합니다. 구성요소는 typing.Annotated 를 이용하여 정의합니다.

list_repository_activites 메소드 같은 경우, 요청은 아래와 같이 이루어집니다.

 1@AsyncSession.single_session("https://api.github.com/", aiohttp.ClientSession)
 2@request("GET", "/repos/{owner}/{repo}/activity")
 3async def list_repository_activites(
 4    session: AsyncSession,
 5    response: Response,
 6    owner: Annotated[str, Path],
 7    repo: Annotated[str, Path],
 8    activity_type: Annotated[str, Query],
 9    authorization: Annotated[str, Header]
10) -> dict[str, Any]:
11    return response.json()
12
13await list_repository_activites(
14    owner="gunyu1019",
15    repo="ahttp_client",
16    activity_type="push",
17    authorization="Bearer <GITHUB TOKEN>"
18)
19
20# curl https://api.github.com/repos/gunyu1019/ahttp_client/activity?activity_type=push \
21#    -H "authorization: Bearer <GITHUB TOKEN>"

참고

HTTP 구성요소(Component)는 typing.Annotated 대신에 typing.Union 으로 정의할 수 있습니다.

@AsyncSession.single_session("https://api.github.com/", aiohttp.ClientSession)
@request("GET", "/repos/{owner}/{repo}/activity")
async def list_repository_activites(
    session: AsyncSession,
    response: Response,
    owner:str | Path,
    repo: str | Path,
    activity_type: str | Query,
    authorization: str | Header  # or, authorization: typing.Union[str, Header]
) -> dict[str, Any]:
    return response.json()

하지만, typing.Anntoated 를 이용하는 것을 권장합니다.

구성 요소(Component)의 용도

  • Header - HTTP 요청에서 header를 정의합니다.

  • Query - URL 매게변수의 값을 삽입하는 용도로 정의됩니다.

  • Path - URL에 지정된 값을 삽입하는 용도로 사용됩니다.

  • Body - 요청 본문 전체를 하나의 값으로 보냅니다. dict, list, tuple 은 JSON으로 인코딩되고, 그 외 값(파일류 객체 포함)은 원본(raw) 그대로 전송됩니다.

  • BodyJson - JSON 요청 본문의 한 필드를 보냅니다. 같은 메소드에 있는 여러 BodyJson 매개변수는 하나의 JSON 객체로 합쳐지고, 중첩된 키가 필요하면 점(.)으로 구분해서 custom_key 에 넘기면 됩니다.

  • BodyForm - Form 요청 본문의 한 필드를 보냅니다. 필드에 파일 메타데이터(metadata 참고)나 파일류 값이 들어가면 자동으로 multipart/form-data 로 바뀌고, 아니면 BodyForm 필드들이 그냥 다같이 URL 인코딩됩니다.

경고

요청 본문(body) 매개변수는 Body, BodyJson, BodyForm 중 하나만 골라 써야 합니다. 같은 메소드에서 Body 를 BodyJson 이나 BodyForm 과 섞어 쓰면 TypeError 예외가 날 수 있습니다.

Custom Component Name

PEP 8 규칙에 따르면, 매개변수는 반드시 밑줄을 사용한 lowercase를 따르도록 하고 있습니다. (참조 문서)

하지만 API에서 parameter 또는 header의 이름은 Camel Case 또는 Pascal Case 일 수 있습니다.

PEP 8 규칙을 따르기 위해, ahttp_client 패키지는 custom component name 기능을 지원합니다.

 1@AsyncSession.single_session("https://api.github.com/", aiohttp.ClientSession)
 2@request("GET", "/repos/{owner}/{repo}/activity")
 3async def list_repository_activites(
 4    session: AsyncSession,
 5    response: Response,
 6    owner: Annotated[str, Path],
 7    repo: Annotated[str, Path],
 8    activity_type: Annotated[str, Query],
 9    token: Annotated[str, Header.custom_name("Authorization")]
10) -> dict[str, Any]:
11    return response.json()
12
13await list_repository_activites(
14    owner="gunyu1019",
15    repo="ahttp_client",
16    activity_type="push",
17    token="Bearer <GITHUB TOKEN>"
18)
19
20# curl https://api.github.com/repos/gunyu1019/ahttp_client/activity?activity_type=push \
21#    -H "Authorization: Bearer <GITHUB TOKEN>"

위에 작성된 예제와 같이, GITHUB API TOKEN 값은 list_repository_activites 의 token 인수로 삽입하였습니다. HTTP 호출 처리 과정에서 token 인수의 값은 매개변수의 이름을 따른 것이 아닌, “Authorization” 를 가지고 header에 삽입되었습니다.

경고

Custom Component Name 기능은 Header, Query, BodyJson, BodyForm 에서만 쓸 수 있습니다. Path 값은 요청 경로의 플레이스홀더와 이름이 같아야 하고 Body 는 애초에 필드 키라는 개념이 없어서, 이 둘은 custom name을 지원하지 않습니다. Path 또는 Body 에 custom_name 을 호출하면 NotImplementedError 예외가 발생합니다.