예외 (Exception)¶
ahttp_client 는 표준 HTTP 4xx/5xx 상태 코드마다 예외 클래스를 미리 만들어둬서, response.status 를 직접 확인하고 커스텀 예외를 던지는 코드를 따로 짤 필요가 없습니다.
1import aiohttp
2
3from ahttp_client import AsyncSession, HTTPNotFound, Response
4
5
6class GithubService(AsyncSession):
7 async def after_request(self, response: Response) -> Response:
8 response.raise_for_status()
9 return response
10
11async with GithubService("https://api.github.com", aiohttp.ClientSession) as service:
12 try:
13 await service.list_repositories(user="does-not-exist")
14 except HTTPNotFound as exc:
15 print(exc.status, exc.url)
Response.raise_for_status() 는 응답 상태 코드에 등록된 예외 클래스를 찾아서, 있으면 응답을 붙여 그 예외를 던집니다. 성공했거나 리다이렉트된 응답(2xx/3xx)은 그냥 놔둡니다.
요청 상태 검증¶
엔드포인트가 HTTP 200만 허용할 때 @request 또는 HTTP 메서드 데코레이터에 raise_on=True 를 지정합니다. 요청 실행 경로는 응답을 반환하거나 역직렬화하기 전에 원본 응답을 검증합니다. 그 밖의 모든 상태는 해당 응답을 담은 예외를 발생시킵니다. 4xx와 5xx 응답은 등록된 하위 클래스(또는 HTTPClientError/HTTPServerError)를 사용하고, 나머지 상태는 기본 HTTPException 을 사용합니다. 예외를 발생시키기 전에 응답을 닫습니다.
class GithubService(AsyncSession):
@get("/health", raise_on=True)
async def health(self) -> None:
...
Response.raise_for_status() 와 달리 raise_on=True 는 201이나 204 같은 다른 성공 상태와 리다이렉트를 포함해 200 이외의 모든 상태를 거부합니다.
예외 계층 구조¶
모든 예외는 HTTPException 을 상속받고, 예외를 유발한 response 와 그 status 코드, url 을 들고 있습니다.
HTTPClientError - 4xx 응답을 위한 기본 클래스입니다. 전용 클래스가 없는 4xx 상태 코드에 직접 사용됩니다.
HTTPServerError - 5xx 응답에 대해서도 같은 방식으로 동작합니다.
표준 상태 코드마다 구체적인 서브클래스가 하나씩 있습니다. HTTPBadRequest (400), HTTPNotFound (404), HTTPTooManyRequests (429), HTTPInternalServerError (500), HTTPServiceUnavailable (503) 같은 식입니다.
참고
RFC 9110에서 이름이 바뀐 상태 코드가 몇 개 있는데, 예전 이름과 지금 이름 둘 다 같은 클래스의 별칭으로 내보내고 있습니다: HTTPPayloadTooLarge/HTTPContentTooLarge (413), HTTPRequestURITooLong/HTTPURITooLong (414), HTTPUnprocessableEntity/HTTPUnprocessableContent (422).
정확한 상태 코드 대신 카테고리 단위로 잡으면, 모든 4xx나 5xx 응답을 똑같이 재시도하거나 로그로 남기고 싶을 때 편합니다:
from ahttp_client import HTTPClientError, HTTPServerError
try:
await service.list_repositories(user="gunyu1019")
except HTTPClientError:
... # any 4xx status
except HTTPServerError:
... # any 5xx status
재시도(Retry)와 함께 사용하기¶
raise_for_status() 가 실패한 응답을 타입 있는 예외로 바꿔주니까, 상태 코드를 직접 검사하는 코드 없이도 그 예외를 retry_on 에 그냥 적어 넣으면 서버 오류에서만 재시도하게 만들 수 있습니다 (재시도 (Retry) 참고):
from ahttp_client import HTTPServerError, retry
class GithubService(AsyncSession):
async def after_request(self, response: Response) -> Response:
response.raise_for_status()
return response
@retry(retry_on=(HTTPServerError,))
@get("/users/{user}/repos")
async def list_repositories(self, response: Response, user: Annotated[str, Path]):
return response.json()
raise_on=True 는 4xx와 5xx 상태에 같은 타입 변환을 적용하면서 200 이외의 모든 상태도 거부하므로, 기본 retry_on=(HTTPServerError,) 설정과 직접 함께 사용할 수 있습니다:
@retry(max_retries=3)
@get("/health", raise_on=True)
async def health(self, response: Response):
return response.json()
예외를 발생시키지 않고 응답을 재시도해야 한다면 retry_on_status 를 사용하세요. 재시도 (Retry) 를 참고하세요.
Reference¶
- class HTTPException¶
성공하지 못한 HTTP 응답을 위한 기본 예외입니다.
각 구체적인 상태 예외는 HTTP
status_code와 사람이 읽기 좋은title을 클래스 변수로 갖고 있습니다.raise_for_status()로 예외가 발생할 때는 원본 응답도 함께 보존됩니다.
- class HTTPClientError¶
기반 클래스:
HTTPException등록되지 않은 4xx 클라이언트 오류 응답을 위한 기본 예외입니다.
- class HTTPServerError¶
기반 클래스:
HTTPException등록되지 않은 5xx 서버 오류 응답을 위한 기본 예외입니다.
- exception_for_status(status_code: int) type[HTTPException] | None¶
성공하지 못한 상태 코드에 연관된 예외 클래스를 반환합니다.
등록된 HTTP 상태 코드는 그에 맞는 구체적인 예외 클래스로, 4xx·5xx 범위의 나머지는 각 카테고리의 기본 클래스로 연결됩니다. 성공하거나 리다이렉트된 응답이면
None을 반환합니다.