HTTPリクエスト、HTTPリクエスト処理、HTTPレスポンスについて。

# やること
webAPIを使ってHTTPリクエストについてアウトプットする。
Json形式でデータを扱いますが、Json形式のデータの扱い方についてもアウトプットする内容

## webAPI叩く側
```
url = "YOUR_URL"
response = requests.get(url)
```

※YOUR_URLにAPIのURLいれる。
responseにAPIからのHTTPレスポンスが入る

## お次はAPI叩かれる側(サーバー側)

クライアントがAPIを叩くと、
YOUR_URL宛にHTTPリクエストがされる。
HTTPリクエストされた側は、リクエストを解析して、処理を開始する。
サーバー側が受け取るHTTPリクエストの例は次の通り。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882753/75620070-bd92-c7f6-861a-897a5908e217.png)


一番上を解釈すると、
ルートキー、メソッド GETなのかPOSTなのか。
GET /tenant/33432?name=arisa&age=25 のようなクエリパスが叩かれた場合は、
rawQueryStringにクエリパスが入る。らしい。

もしroutKeyをIF文で判定したい場合、
```
if event['routeKey'] == "GET /tenant/{id}"
```

これで、routeKyeを判定できる。
eventの中のrouteKeyってかんじで書く。

----------------------
よく使うpathParametersの取得方法は、
```
print(event['pathParameters']['id'])
```
eventの中のpathParaの中のidって感じで書く。

------------------------
もしクライアントがなんのブラウザから接続したいか見たい場合は、userAgentを取得する。
```
user_agent = event["requestContext"]["http"]["userAgent"]
print(f"user_agent: {user_agent}")
```


ただし、ローカル環境内でAPIを叩いたときは。。。

HTTPリクエスト情報は小さくなる。
```
event: 
{ "routeKey": "GET /tenant/{id}", "pathParameters": { "id": "33432" } }
```
この中にクライアント情報のevent["requestContext"]["http"]["userAgent"]が入っていないのでエラーがおきます。

エラーログ
```
[ERROR] KeyError: 'requestContext'
Traceback (most recent call last):
  File "/var/task/lambda_function.py", line 16, in lambda_handler
    user_agent = event["requestContext"]["http"]["userAgent"]
```


これを回避するには、プログラムを変えます。					
```
user_agent = event.get("requestContext", {}).get("http", {}).get("userAgent", "Unknown")
print(f"user_agent: {user_agent}")					
```
getの見方。					
◯event.get("requestContext", {})				
requestContextがあればそのまま値を返し、なければ空の辞書を返す。{}	
◯.get("http", {})				
上に同じ	
◯.get("userAgent", "Unknown")				
なければUnknownを返す。	


ローカルから実行した場合のログ、↓
```
START RequestId: 03d21791-012d-455c-bce1-990748370346 Version: $LATEST
event: {"routeKey": "GET /tenant/{id}", "pathParameters": {"id": "33432"}}
Unknown
```
routeKey、パラメータ、とれて、userAgentはunknownになっているので成功。


## APIサーバがクライアントに返す形

最終的にresが返されるとすると、
```
res = {			
        "statusCode": statusCode,			
        "headers": {			
            "Content-Type": "application/json"			
        },			
        "body": body			
    }			
    return res			
```
これの説明

◯statusCode	
HTTP処理がサーバーで成功したかどうかを返す。200なら成功というのが共通認識
◯headers	
Content-Type: "application/json" これはjson形式でデータ返しますよ～ということ。 webAPIだからjson形式でデータ返してるけど。グーグルなどwebページにアクセスしたときは Content-Type: text/html が返ってくる。	
◯body	
リクエストによってサーバ側が処理した結果をbodyに載せてクライアントに返信する。	

## bodyはどうやって作られるか
実際のプログラム↓
```
table = dynamodb.Table('YOUR_TABLE')
if event['routeKey'] == "GET /tenant/{id}":								
								
            job="ID取得"								
								
            body = table.get_item(								
                Key={'tenant_id': event['pathParameters']['id']})			
            body = body["Item"]								
            responseBody = [								
                {'tenant_id': body['tenant_id'], 'tenant_name': body['tenant_name']}]		
            body = responseBody								
            result_response="IDを指定して取得成功"								
```

説明---
```
body = table.get_item( Key={'tenant_id': event['pathParameters']['id']} )
```
変数tableにはあらかじめテーブルが代入されています。
get_item メソッドを使って、指定した tenant_id に対応するデータを取得する処理。
キー検索して一件のみ取得できる。

---------------------------

body = body["Item"]とは、
これをする前のbodyと比較してbody = body["Item"]の意味を見ていこう。

```
{'Item': {'employee': Decimal('5'), 'tenant_name': 'Test Tenant', 'tenant_id': '33432'}, 'ResponseMetadata': {'RequestId': '2HI22IT58GITUJ9O8SH3V2PS6JVV4KQNSO5AEMVJF66Q9ASUAAJG', 'HTTPStatusCode': 200, 'HTTPHeaders': {'server': 'Server', 'date': 'Tue, 11 Feb 2025 07:28:51 GMT', 'content-type': 'application/x-amz-json-1.0', 'content-length': '91', 'connection': 'keep-alive', 'x-amzn-requestid': '2HI22IT58GITUJ9O8SH3V2PS6JVV4KQNSO5AEMVJF66Q9ASUAAJG', 'x-amz-crc32': '41071039'}, 'RetryAttempts': 0}}
```

body = body["Item"]をした後、
```
{'employee': Decimal('5'), 'tenant_name': 'Test Tenant', 'tenant_id': '33432'}
```

つまり、Item項目だけ抜き出している。

--------

```
responseBody = [			
                {'tenant_id': body['tenant_id'], 'tenant_name': body['tenant_name']}]			
            body = responseBody			
```
リスト形式で必要なデータを格納する。			
			
			
△もし、キーを使って一意の明細を取り出すのではなく、複数明細を取り出すケースを考えてみる。			

```
response = table.query( KeyConditionExpression=Key('tenant_id').eq(event['pathParameters']['id']) )			

# 複数のデータを取得			
items = response['Items'] 			
# IDとNameだけをリストに格納			
responseBody = [{'tenant_id': item['tenant_id'], 'tenant_name': item['tenant_name']} for item in items]			
````
テーブルにクエリして、tenant_id が 指定されたIDと一致するすべてのデータ をリストに格納する。
responseの中からItems情報のみ取得。
responseBodyに必要な情報のみリストとして入れる。

## API叩いた側(HTTPレスポンスに対しての処理)
クライアントが、
response = requests.get(url)
したら、
サーバーが
resを返してくれます。
クライアントがresをどう処理するかについての話です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882753/fd097ff2-e2aa-e706-6b37-34ee6967c1fb.png)

サーバー側が返したresはクライアント側のresponseに入れます。

responseには、		
ステータスコード		response.status_code
レスポンスヘッダー		response.headers
レスポンスボディ		response.text
が含まれます。		

プリント文で出力した決は次の通り
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882753/d32aa89c-f271-c52a-5bf6-104e0ee8057a.png)

200が response.status_code
{'Date'が response.headers
tenant_idが response.text

### body(response.text)から必要なデータを取り出してみる。そのためにresponseを整形する

JSON形式のデータ整形をします。このままでは使いにくいため整形する。

```
json.loads(response.text)
```
これをすると、response.textを文字列から辞書型に変更できる。
↓json.loads(response.text)やる前とやったあとです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882753/71b41c5b-96fb-69a6-3aa0-f94bf9a77e9a.png)

""がとれて、文字列から辞書型になりました。

まだ、bodyの中が文字列なので、もいっかいjson.loadsしてデコードする。
```
 body_data = json.loads(data) 
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882753/ae0d1eb6-ed30-ea73-c088-49d9705fc111.png)

### bodyからある項目（ここではidと、nameのみのリストを作る）

bodyからある項目（ここではidと、nameのみのリストを作る）	
```
tenant_list = []	
    for tenant in body_data:	
        tenant_list.append((tenant["tenant_id"], tenant["tenant_name"]))	
    print(tenant_list)	
```
tenant_listに整形したresponseである body_data からtenant_idとtenant_nameを全件入れる。


### 抽出したJSONからあるキーの明細のみ取り出す。		
```	
    matching_tenants = [] 		
    for tenant in body_data:		
        if tenant["tenant_id"] == "33432":		
            index = body_data.index(tenant) 		
            matching_tenants.append(tenant)		
    print(index)		
    print(matching_tenants)		
```
body_dataの中をtenantが上から下へ移動する。
tenantのtenant_idが33432のとき、tenantをリストに追加する。
マッチしたindexはtenantが上から何番目に存在したのかを取得する。
結果↓

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882753/a8c1b80f-1a01-726b-c572-8aa11879a4e4.png)

ほしいデータが取れた！

## 感想 
HTTPリクエスト、レスポンスについて理解が深まった。
HTTPのプロパティについて学べばもっとオモロイことができそう。