অধ্যায় 3 · HTTP ও ওয়েব API
HTTP মেথড, স্ট্যাটাস কোড, হেডার ও JSON
- পৃষ্ঠা 12 / 22
- 15 মিনিট পড়া
আগের পাতায় আপনি একটা HTTP মেসেজের আকার দেখেছেন: একটা শুরুর লাইন, হেডার, একটা খালি লাইন, একটা বডি। এই পাতায় আসবে সেই চারটা জিনিস, যারা একটা API কলের প্রায় পুরো অর্থ বহন করে: মেথড (আপনি কী করতে চান), স্ট্যাটাস কোড (কাজটা কেমন হলো), হেডার (মেসেজের আশপাশের খুঁটিনাটি) আর বডি, যা API-র ক্ষেত্রে প্রায় সবসময় JSON।
একটা LLM কল ব্যর্থ হলে আপনি ঠিক এই তথ্যগুলোই পড়েন। 401 মানে আপনার কি ভুল; 400 প্রায়ই মানে প্রম্পট খুব লম্বা বা কোনো প্যারামিটার অবৈধ; 429 মানে "আস্তে চলুন", পরে আবার চেষ্টা (retry) করতে হবে; 503 মানে প্রোভাইডারের ওপর চাপ বেশি, আবার চেষ্টা করলে হয়তো কাজ হবে। যে কোড এগুলোকে একইভাবে দেখে, সে হয় খুব তাড়াতাড়ি হাল ছেড়ে দেয়, নয়তো যে সার্ভিস থামতে বলছে তাকে অবিরাম রিকোয়েস্ট দিয়ে চাপ দিতে থাকে।
যা শিখবেন
- HTTP মেথড GET, POST, PUT, PATCH আর DELETE, আর কোনগুলো বারবার পাঠানো নিরাপদ।
- স্ট্যাটাস কোডের পাঁচটা পরিবার, আর API-তে সবচেয়ে বেশি দেখা ১৫টা কোড।
- সবচেয়ে জরুরি হেডারগুলো:
Content-Type,Accept,Authorization,User-Agent। - JSON: এর ছয় ধরনের মান, Python-এ কোনটা কী হয়, আর কোন ভুলে JSON অবৈধ হয়ে যায়।
- পাথ প্যারামিটার, কোয়েরি প্যারামিটার আর রিকোয়েস্ট বডির পার্থক্য, আর প্রচলিত কনটেন্ট টাইপ।
প্রতিটা উদাহরণে ব্যবহার হবে http://localhost:8800-এর Practice API (সেট আপ করা হবে ওয়েব API ও REST: ডকুমেন্টেশন পড়া আর curl দিয়ে পরীক্ষা পাতায়)। এর দুটো এন্ডপয়েন্ট (endpoint) আছে শুধু শেখার জন্য: /v1/echo যা পেয়েছে তা-ই ফেরত পাঠায় (মেথড, কোয়েরি, কয়েকটা হেডার আর JSON বডি), আর /v1/status/{code} আপনি যে স্ট্যাটাস কোড চাইবেন সেটাই দিয়ে উত্তর দেয়।
মেথড: আপনি কী করতে চান?
মেথড হলো রিকোয়েস্টের ক্রিয়াপদ। পাথ একটা জিনিসের (একটা রিসোর্স) নাম বলে; মেথড বলে সেটা নিয়ে কী করতে হবে।
| মেথড | মানে | উদাহরণ | বডি? | সেফ | আইডেমপোটেন্ট |
|---|---|---|---|---|---|
GET | পড়া | GET /v1/courses/2 | না | হ্যাঁ | হ্যাঁ |
POST | তৈরি করা, বা "এই কাজটা করো" | POST /v1/reviews, POST /v1/chat | হ্যাঁ | না | না |
PUT | পুরো রিসোর্স বদলে দেওয়া | সব ফিল্ডসহ PUT /v1/courses/2 | হ্যাঁ | না | হ্যাঁ |
PATCH | কিছু ফিল্ড বদলানো | {"hours": 14} দিয়ে PATCH /v1/courses/2 | হ্যাঁ | না | সাধারণত না |
DELETE | মুছে ফেলা | DELETE /v1/reviews/7 | কদাচিৎ | না | হ্যাঁ |
- সেফ (safe) মানে "শুধু পড়ে, সার্ভারে কিছুই বদলায় না"। GET যতবার খুশি পাঠাতে পারেন।
- আইডেমপোটেন্ট (idempotent) মানে "দুবার পাঠালেও ফল একবার পাঠানোর মতোই"। রিভিউ ৭ দুবার মুছলেও সেটা মোছাই থাকে; একই পুরো রেকর্ড দুবার PUT করলে রেকর্ড একই থাকে। POST-এর কোনোটাই নয়: "একটা রিভিউ তৈরি করো" দুবার পাঠালে দুটো রিভিউ তৈরি হয়, আর একটা চ্যাট রিকোয়েস্ট দুবার পাঠালে বিলও দুবার হয়।
বাস্তবে পার্থক্যটা এ কারণেই জরুরি: একটা রিকোয়েস্ট টাইমআউট হলে আপনি জানেন না সার্ভার কাজটা করেছে কি না। GET আবার পাঠানো নিরীহ; POST আবার পাঠালে একটা পেমেন্ট বা LLM বিল দ্বিগুণ হতে পারে। রিট্রাই (তিন পাতা পরে) ডিফল্টভাবে নিরাপদ শুধু সেফ আর আইডেমপোটেন্ট রিকোয়েস্টের জন্য। এ ছাড়া HEAD (GET-এর মতো, শুধু হেডার) আর OPTIONS (এখানে কী কী করা যায়?) দেখবেন, যেগুলো মূলত টুল আর ব্রাউজার ব্যবহার করে।
curl দিয়ে মেথড
আপনি অন্য কিছু না বললে curl GET পাঠায়। -X দিয়ে মেথড বাছাই করা হয়; --json একটা JSON বডি পাঠায় (আর ধরে নেয় মেথড POST)। echo এন্ডপয়েন্টকে জিজ্ঞেস করুন সে কী পেয়েছে:
curl -s -X DELETE "http://localhost:8800/v1/echo?id=7"
echo
curl -s -X PUT http://localhost:8800/v1/echo --json '{"title": "Python for AI", "hours": 12}'
echo
curl -s -X PATCH http://localhost:8800/v1/echo --json '{"hours": 14}'{"method":"DELETE","query":{"id":"7"},"headers":{"user-agent":"curl/8.5.0","accept":"*/*"},"json":null}
{"method":"PUT","query":{},"headers":{"user-agent":"curl/8.5.0","content-type":"application/json","accept":"application/json"},"json":{"title":"Python for AI","hours":12}}
{"method":"PATCH","query":{},"headers":{"user-agent":"curl/8.5.0","content-type":"application/json","accept":"application/json"},"json":{"hours":14}}খেয়াল করুন, --json আপনার হয়ে দুটো হেডারও বসিয়ে দিয়েছে: content-type: application/json আর accept: application/json। (echo শুধু আউটপুটগুলোর মাঝে একটা লাইন ব্রেক দেয়। user-agent-এ আপনার curl-এর ভার্সন দেখাবে, যা 8.5.0 থেকে আলাদা হতে পারে।)
কোন পাথ কোন মেথড নেবে, তা ঠিক করে সার্ভার। অনুমতি নেই এমন মেথড পাঠালে পাবেন 405 Method Not Allowed, সাথে একটা allow হেডার, যাতে লেখা থাকে কোনগুলো চলবে:
curl -si -X DELETE http://localhost:8800/v1/courses/2 | grep -v '^date' | tr -d '\r'HTTP/1.1 405 Method Not Allowed
server: uvicorn
allow: GET
content-length: 31
content-type: application/json
{"detail":"Method Not Allowed"}স্ট্যাটাস কোড: কাজটা কেমন হলো?
প্রথম অঙ্কটা বলে দেয় কোন পরিবার। অন্য কিছুর আগে এটা পড়ুন:
| পরিবার | মানে | সমস্যা কার? |
|---|---|---|
1xx | তথ্যমূলক (API-তে বিরল) | কারও নয় |
2xx | সফল | কারও নয় |
3xx | রিডাইরেকশন: অন্য জায়গায় দেখুন, বা আপনার ক্যাশ করা কপি ব্যবহার করুন | কারও নয়; নির্দেশটা অনুসরণ করুন |
4xx | ক্লায়েন্টের ভুল: রিকোয়েস্টটাই ভুল ছিল | আপনার: রিকোয়েস্ট ঠিক করুন। না বদলে আবার পাঠালে আবারও ব্যর্থ হবে (429 ব্যতিক্রম) |
5xx | সার্ভারের ভুল: রিকোয়েস্ট হয়তো ঠিক ছিল, সার্ভার ব্যর্থ হয়েছে | ওদের: প্রায়ই সাময়িক, পরে রিট্রাই করলে কাজ হতে পারে |
Practice API-র /v1/status/{code} আপনি যে কোড চান সেটাই ফেরত দেয়। -o /dev/null বডিটা ফেলে দেয় আর -w '%{http_code}' শুধু কোডটা প্রিন্ট করে; স্ক্রিপ্টে এই জুটি প্রায়ই ব্যবহার করবেন:
for code in 200 201 204 404 429 503; do
curl -s -o /dev/null -w "asked for $code, got %{http_code}\n" "http://localhost:8800/v1/status/$code"
doneasked for 200, got 200
asked for 201, got 201
asked for 204, got 204
asked for 404, got 404
asked for 429, got 429
asked for 503, got 503সবচেয়ে বেশি যে কোডগুলো দেখবেন, আর একটা AI বা ব্যাকএন্ড প্রজেক্টে সাধারণত সেগুলোর মানে কী:
| কোড | নাম | সাধারণ মানে |
|---|---|---|
200 | OK | কাজ হয়েছে; উত্তর বডিতে। |
201 | Created | একটা POST কিছু তৈরি করেছে (একটা রিভিউ, একজন ইউজার)। প্রায়ই সেটার দিকে নির্দেশ করা একটা Location হেডার থাকে। |
204 | No Content | কাজ হয়েছে, আর ইচ্ছা করেই কোনো বডি নেই (DELETE-এ সাধারণ)। |
301 | Moved Permanently | রিসোর্সটা এখন অন্য URL-এ (Location হেডারে)। curl -L সেটা অনুসরণ করে। |
304 | Not Modified | "আপনার ক্যাশ করা কপিটাই এখনো ঠিক আছে", বডি নেই। শর্তসাপেক্ষ রিকোয়েস্টের উত্তর (অ্যাডভান্সড টিউটোরিয়ালে)। |
400 | Bad Request | রিকোয়েস্টটা বিকৃত বা অবৈধ। অজানা প্যারামিটার, বা মডেলের জন্য খুব লম্বা প্রম্পট হলে LLM API এটা দেয়। |
401 | Unauthorized | পরিচয় যাচাই হয়নি: API কি বা টোকেন নেই, ভুল, বা মেয়াদোত্তীর্ণ। (নামটা ঐতিহাসিক; এর মানে "আপনি কে?") |
403 | Forbidden | আমরা জানি আপনি কে, আর এই কাজের অনুমতি আপনার নেই। |
404 | Not Found | এমন কোনো রিসোর্স নেই (বা পাথে টাইপো, বা ভুল মডেলের নাম)। |
409 | Conflict | বর্তমান অবস্থার সাথে সংঘাত: ইউজারনেমটা আগেই নেওয়া, রেকর্ডটা এর মধ্যে বদলে গেছে। |
422 | Unprocessable Content | JSON ঠিকঠাক গঠিত, কিন্তু মানগুলো ভ্যালিডেশনে টেকে না (৫-এর মধ্যে ৯ রেটিং, দরকারি ফিল্ড বাদ পড়া)। FastAPI এটা অনেক ব্যবহার করে। |
429 | Too Many Requests | রেট লিমিটে পৌঁছে গেছেন। অপেক্ষা করুন (Retry-After হেডার দেখুন), তারপর রিট্রাই। অ্যাকাউন্টে ক্রেডিট শেষ হলেও OpenAI 429 দেয়; কোনটা, তা বডিতে লেখা থাকে। |
500 | Internal Server Error | সার্ভারে বাগ বা ক্র্যাশ। আপনার দোষ নয়; বারবার হলে জানান। |
502 | Bad Gateway | সামনে থাকা একটা প্রক্সি বা লোড ব্যালান্সার পেছনের আসল সার্ভার থেকে খারাপ উত্তর পেয়েছে। |
503 | Service Unavailable | অতিরিক্ত চাপ বা রক্ষণাবেক্ষণ চলছে; সাময়িক। পরে রিট্রাই করুন। (Anthropic "overloaded" বোঝাতে অ-স্ট্যান্ডার্ড 529 ব্যবহার করে।) |
অপেক্ষা করে রিট্রাই করুন: 429, 500, 502, 503, 504 (gateway timeout)। না বদলে রিট্রাই করবেন না: 400, 401, 403, 404, 409, 422; বরং রিকোয়েস্ট ঠিক করুন।
স্ট্যাটাস কোড আর বডি একসাথে কাজ করে
একটা ভালো API একটা পরিষ্কার স্ট্যাটাস কোড আর সেটা ব্যাখ্যা করা একটা বডি দুটোই ফেরত দেয়। Practice API যা দিয়ে বানানো, সেই FastAPI (যা আপনি নিজে ব্যবহার করবেন আপনার প্রথম FastAPI অ্যাপ থেকে) ব্যাখ্যাটা রাখে detail-এর নিচে:
curl -s -w " -> %{http_code}\n" http://localhost:8800/v1/courses/99
curl -s -w " -> %{http_code}\n" http://localhost:8800/v1/courses/abc{"detail":"Course 99 not found"} -> 404
{"detail":[{"type":"int_parsing","loc":["path","course_id"],"msg":"Input should be a valid integer, unable to parse string as an integer","input":"abc"}]} -> 422প্রথমটা 404: পাথ ঠিক ছিল, কিন্তু কোর্স ৯৯ নেই। দ্বিতীয়টা 422: কোর্স আইডি পূর্ণসংখ্যা, আর abc পূর্ণসংখ্যা নয়। বডি ঠিক কোথায় (loc: পাথ প্যারামিটার course_id) আর কেন, দুটোই বলে দেয়। এরর রেসপন্সের বডি সবসময় পড়ুন; সাধারণত সমাধানটা ওখানেই লেখা থাকে।
আর একটা 204-এর হেডারের পরে সত্যিই কিছু থাকে না:
curl -si http://localhost:8800/v1/status/204 | grep -v '^date' | tr -d '\r'HTTP/1.1 204 No Content
server: uvicornযে হেডারগুলো গুরুত্বপূর্ণ
হেডার হলো মেটাডেটা: মেসেজ সম্পর্কে তথ্য। এদের নাম কেস-ইনসেনসিটিভ। সবচেয়ে বেশি যেগুলো বসাবেন আর পড়বেন:
| হেডার | কে পাঠায় | মানে | উদাহরণ |
|---|---|---|---|
Content-Type | যে বডি পাঠায় | এই বডির ফরম্যাট | application/json |
Accept | ক্লায়েন্ট | ক্লায়েন্ট কোন ফরম্যাটে উত্তর চায় | application/json, text/event-stream |
Authorization | ক্লায়েন্ট | ক্রেডেনশিয়াল, সাধারণত একটা বেয়ারার টোকেন | Bearer token-nadia-001 |
X-API-Key (ও এ রকম অন্যগুলো) | ক্লায়েন্ট | একটা API কি, যেসব API নিজস্ব হেডার ব্যবহার করে তাদের জন্য | practice-key-123; Anthropic ব্যবহার করে x-api-key |
User-Agent | ক্লায়েন্ট | কোন প্রোগ্রাম জিজ্ঞেস করছে (আর তার ভার্সন) | curl/8.5.0, python-requests/2.34.2 |
Content-Length | যে বডি পাঠায় | বডির আকার, বাইটে | 75 |
Location | সার্ভার | নতুন বা সরে যাওয়া রিসোর্স কোথায় | /v1/reviews/1 |
Retry-After | সার্ভার | আবার চেষ্টার আগে কত সেকেন্ড অপেক্ষা করতে হবে (429/503-এর সাথে) | 10 |
X- দিয়ে শুরু হওয়া হেডারগুলো কোনো API-র নিজের বানানো। যেকোনো হেডার যোগ করতে -H "Name: value"; -A হলো User-Agent-এর শর্টকাট। কী পৌঁছাল, echo এন্ডপয়েন্ট দেখায় (Python-এর json.tool দিয়ে সাজিয়ে):
curl -s http://localhost:8800/v1/echo \
-H "Authorization: Bearer test-token" \
-H "Accept: application/json" \
-A "nadia-notes/0.1" | python -m json.tool{
"method": "GET",
"query": {},
"headers": {
"user-agent": "nadia-notes/0.1",
"authorization": "Bearer test-token",
"accept": "application/json"
},
"json": null
}দুটো অভ্যাস এখনই গড়ে তোলার মতো। এক, আপনার স্ক্রিপ্টকে একটা অর্থবহ User-Agent দিন (my-app/1.2 (contact@example.com)); API-র মালিকেরা এটা দেখে আপনাকে ব্লক না করে যোগাযোগ করেন। দুই, সিক্রেট যায় হেডারে, কখনো URL-এ নয়: URL লেখা হয় সার্ভারের লগে, ব্রাউজারের হিস্ট্রিতে আর প্রক্সিতে; হেডার সাধারণত হয় না।
JSON-এর মূল কথা
JSON (JavaScript Object Notation) হলো প্রায় সব API বডির টেক্সট ফরম্যাট। এতে মান আছে ছয় রকমের, আর প্রতিটা একটা Python টাইপে মেলে:
| JSON | উদাহরণ | Python |
|---|---|---|
| object | {"id": 2, "title": "RAG"} | dict |
| array | ["python", "ai"] | list |
| string | "Python for AI" (শুধু ডাবল কোটেশন) | str |
| number | 12, 4.8, -1e3 | int বা float |
| boolean | true, false (ছোট হাতের) | True, False |
| null | null | None |
অবজেক্ট আর অ্যারে যেকোনো গভীরতায় একটার ভেতর আরেকটা থাকতে পারে: একটা চ্যাট API-র রেসপন্স হলো এমন একটা অবজেক্ট, যার ভেতরে মেসেজের একটা অ্যারে, আর প্রতিটা মেসেজ আবার একটা অবজেক্ট। Python-এ json.dumps Python মানকে JSON টেক্সটে বদলায়, আর json.loads JSON টেক্সটকে আবার Python মানে বদলায় (দুটোই আপনি Python for AI-তে ব্যবহার করেছেন):
import json
course = {"id": 2, "title": "Python for AI", "free": False,
"tags": ["python", "ai"], "discount": None, "rating": 4.8}
text = json.dumps(course)
print(text)
print(type(text).__name__, len(text))
back = json.loads(text)
print(back == course, back["tags"][1])
print(json.dumps({"city": "ঢাকা"}))
print(json.dumps({"city": "ঢাকা"}, ensure_ascii=False)){"id": 2, "title": "Python for AI", "free": false, "tags": ["python", "ai"], "discount": null, "rating": 4.8}
str 109
True ai
{"city": "\u09a2\u09be\u0995\u09be"}
{"city": "ঢাকা"}যা খেয়াল করবেন: False হয়ে গেছে false, আর None হয়ে গেছে null। পার্স না করা পর্যন্ত JSON স্রেফ একটা str। ডিফল্টভাবে json.dumps ইংরেজি নয় এমন অক্ষরকে \uXXXX আকারে এস্কেপ করে; দুটো রূপই বৈধ JSON আর মানে একই, তবে ফাইল সেভ করার সময় ensure_ascii=False বাংলাকে পড়ার উপযোগী রাখে।
কোন ভুলে JSON অবৈধ হয়
JSON, Python-এর চেয়ে কড়া। নিচের তিনটা একজন Python প্রোগ্রামারের চোখে ঠিকই লাগে, অথচ তিনটাই বাতিল হয়:
bad_examples = [
"{'title': 'RAG'}",
'{"id": 2 "title": "RAG"}',
'{"free": True}',
]
for text in bad_examples:
try:
json.loads(text)
except json.JSONDecodeError as exc:
print(f"{text:26} -> {exc}"){'title': 'RAG'} -> Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
{"id": 2 "title": "RAG"} -> Expecting ',' delimiter: line 1 column 10 (char 9)
{"free": True} -> Expecting value: line 1 column 10 (char 9)সিঙ্গেল কোটেশন, একটা কমা বাদ পড়া, আর true-এর বদলে Python-এর True। শেষ আইটেমের পরে বাড়তি কমাও ({"title": "RAG",}) বাতিল হয়। সমাধান প্রতিবার একই: Python স্ট্রিংয়ের ভেতরে কখনো হাতে JSON লিখবেন না; একটা dict বানান আর টেক্সটটা তৈরি করতে দিন json.dumps-কে (বা requests-এর json=-কে)। যে LLM আউটপুটের "JSON হওয়ার কথা", সেটাও ঠিক এই ভুলগুলোতেই ব্যর্থ হয়; এ কারণেই একে সবসময় try/except-এর ভেতরে পার্স করবেন।
ডেটা কোথায় যায়: পাথ, কোয়েরি নাকি বডি
একটা রিকোয়েস্ট তিন জায়গায় ডেটা বহন করতে পারে। প্রতিটার আলাদা কাজ:
| জায়গা | উদাহরণ | যে কাজে ব্যবহার করবেন |
|---|---|---|
| পাথ প্যারামিটার | /v1/courses/2 | কোন রিসোর্স: একটা জিনিসকে চিহ্নিত করা আইডি। |
| কোয়েরি প্যারামিটার | /v1/courses?level=advanced&per_page=2 | একটা কালেকশন কীভাবে দেখাতে হবে: ফিল্টার, সাজানো, পাতা, অপশন। ছোট, গোপন নয় এমন মান। |
| বডি | {"course_id": 2, "rating": 5, "comment": "..."} | আপনি যে ডেটা পাঠান: নতুন রেকর্ড, একটা চ্যাট কথোপকথন, বড়, নেস্টেড বা গোপন যেকোনো কিছু। |
ফিল্টার হলো কোয়েরি প্যারামিটার:
curl -s "http://localhost:8800/v1/courses?level=advanced&per_page=2" | python -m json.tool{
"page": 1,
"per_page": 2,
"total": 3,
"next_page": 2,
"items": [
{
"id": 8,
"title": "RAG Systems",
"level": "advanced",
"hours": 10,
"rating": 4.9
},
{
"id": 9,
"title": "AI Agents",
"level": "advanced",
"hours": 12,
"rating": 4.8
}
]
}কোয়েরির মানে স্পেস বা চিহ্ন থাকলে এনকোড করার কাজ curl-কে দিন। -G curl-কে বলে -d/--data-urlencode-এর মানগুলো বডিতে না দিয়ে একটা GET-এর কোয়েরি স্ট্রিংয়ে বসাতে:
curl -s -G http://localhost:8800/v1/echo --data-urlencode "q=what is RAG?" -d lang=bn{"method":"GET","query":{"q":"what is RAG?","lang":"bn"},"headers":{"user-agent":"curl/8.5.0","accept":"*/*"},"json":null}সার্ভার প্রশ্নটা হুবহু টাইপ করা অবস্থাতেই পেয়েছে, স্পেস আর ?-সহ, কারণ curl এটা পাঠিয়েছে q=what%20is%20RAG%3F হিসেবে।
রিকোয়েস্ট বডি আর কনটেন্ট টাইপ
বডি স্রেফ কিছু বাইট; Content-Type হেডার প্রাপককে বলে সেগুলো কীভাবে পড়তে হবে। না জানিয়ে JSON পাঠানো একটা ক্লাসিক ভুল। সাধারণ -d বডিটাকে একটা ওয়েব ফর্ম হিসেবে চিহ্নিত করে পাঠায়:
curl -s http://localhost:8800/v1/echo -d '{"name": "Nadia"}'
echo
curl -s http://localhost:8800/v1/echo -H "Content-Type: application/json" -d '{"name": "Nadia"}'{"method":"POST","query":{},"headers":{"user-agent":"curl/8.5.0","accept":"*/*","content-type":"application/x-www-form-urlencoded"},"json":{"name":"Nadia"}}
{"method":"POST","query":{},"headers":{"user-agent":"curl/8.5.0","accept":"*/*","content-type":"application/json"},"json":{"name":"Nadia"}}echo এন্ডপয়েন্ট উদার, দুবারই JSON পার্স করেছে; কিন্তু প্রথম content-type-টা দেখুন: application/x-www-form-urlencoded। একটা আসল API (FastAPI-সহ) এই রিকোয়েস্ট 422 দিয়ে বাতিল করত, কারণ তাকে বলা হয়েছে বডিটা একটা ফর্ম। --json ব্যবহার করুন, অথবা -H "Content-Type: application/json" -d …। আরও খেয়াল করুন, -d নিজে থেকেই মেথড বদলে POST করে দিয়েছে।
| কনটেন্ট টাইপ | বডিটা কী | কোথায় দেখবেন |
|---|---|---|
application/json | JSON টেক্সট | প্রায় সব API, সব LLM API-সহ |
application/x-www-form-urlencoded | name=Nadia&city=Dhaka | পুরোনো ধাঁচের HTML ফর্ম, OAuth টোকেন এন্ডপয়েন্ট |
multipart/form-data | কয়েকটা অংশ, প্রতিটার নিজস্ব হেডার; ফাইল রাখা যায় | ফাইল আপলোড: RAG-এর জন্য একটা PDF, স্পিচ-টু-টেক্সটের জন্য অডিও ফাইল (curl -F "file=@notes.pdf") |
text/plain | সাধারণ টেক্সট | সাধারণ টুল, লগ |
text/event-stream | data: … ইভেন্টের একটা স্ট্রিম (Server-Sent Events) | টোকেন ধরে ধরে LLM-এর উত্তর স্ট্রিম করা (অ্যাডভান্সড টিউটোরিয়ালে) |
টেক্সট ফরম্যাটের কনটেন্ট টাইপে একটা ক্যারেক্টার সেটও থাকতে পারে, যেমন application/json; charset=utf-8। JSON সংজ্ঞাগতভাবেই UTF-8, তাই বাংলা টেক্সট নির্বিঘ্নে যায়।
কোথায় কাজে লাগবে
- প্রতিটা LLM API কল হলো একটা
POST, যাতে থাকেContent-Type: application/json, একটাAuthorization(বাx-api-key) হেডার, আর মডেলের নাম ও মেসেজসহ একটা JSON বডি। উত্তর আসে JSON-এ, টেক্সট আর টোকেন ব্যবহারের হিসাবসহ। - এরর হ্যান্ডলিং হলো স্ট্যাটাস পরিবার ধরে একটা সুইচ: 2xx হলে ব্যবহার করুন; 4xx হলে রিকোয়েস্ট বা কি ঠিক করুন (বডি লগ করুন); 429 আর 5xx হলে অপেক্ষা করে রিট্রাই। তিন পাতা পরে Python-এ ঠিক এটাই লিখবেন।
- নিজের API ডিজাইন করা (ব্যাকএন্ড কী করে: রিকোয়েস্ট, রাউট ও রেসপন্স থেকে): GET নাকি POST, পাথ নাকি কোয়েরি, আর 201/204/404/422 ঠিকঠাক বেছে নেওয়াই একটা API-কে ব্যবহারে আরামদায়ক করার বড় অংশ।
- AI-এর জন্য আপলোড: এমবেড করার জন্য একটা ডকুমেন্ট বা ট্রান্সক্রাইব করার জন্য একটা অডিও ক্লিপ পাঠানো হলো একটা
multipart/form-dataPOST।
সাধারণ ভুল
Content-Type: application/jsonছাড়া JSON পাঠানো।curl -d '{…}'এটাকে ফর্ম হিসেবে চিহ্নিত করে, আর সার্ভার 415 বা 422 দেয়।curl --jsonব্যবহার করুন, অথবা Python-এrequests.post(url, json=data), যা হেডারটা নিজেই বসায়।- সব এররে রিট্রাই করা। 400, 401 বা 422 রিট্রাই করলে আবার ব্যর্থ হয় (আর আপনার রেট লিমিটেও গোনা হতে পারে)। 429 আর 5xx রিট্রাই করুন, অপেক্ষাসহ; 4xx ঠিক করুন।
- কিছু বদলায় এমন কাজে GET ব্যবহার। ক্রলার, লিংক প্রিভিউ আর ব্রাউজার নির্দ্বিধায় GET পাঠায় আর বারবার পাঠায়। যা কিছু তৈরি করে, বদলায়, মোছে বা টাকা খরচ করায়, তা হতে হবে POST/PUT/PATCH/DELETE।
- URL-এ সিক্রেট বা বড় ডেটা রাখা। কোয়েরি স্ট্রিং লগে জমা হয়, আর এর দৈর্ঘ্যের সীমা আছে। কি যায় হেডারে; বড় বা গোপন ডেটা যায় বডিতে।
- হাতে JSON লেখা। সিঙ্গেল কোটেশন, শেষের বাড়তি কমা আর
True/NoneJSON-কে অবৈধ করে। Python অবজেক্ট বানিয়েjson.dumpsদিয়ে সিরিয়ালাইজ করুন।
নিজে চেষ্টা করুন
- সহজ: প্রতিবার একটা curl কমান্ড দিয়ে শুধু স্ট্যাটাস কোড প্রিন্ট করুন, প্রথমে
/v1/courses/5-এর, তারপর/v1/courses/50-এর। কোনটা কোন পরিবারের? - মাঝারি:
/v1/echo-তে একটা POST পাঠান: JSON বডি{"course_id": 2, "rating": 5}, একটা হেডারX-API-Key: practice-key-123, আর কোয়েরি প্যারামিটারdry_run=true। echo-তে দেখুন তিনটাই পৌঁছেছে কি না। - কঠিন: একটা Python ফাংশন
advice(code)লিখুন, যা 2xx-এর জন্য"success", 429 আর 5xx-এর জন্য"retry later", বাকি 4xx-এর জন্য"fix the request", আর 3xx-এর জন্য"follow or use cache"ফেরত দেয়। 200, 204, 304, 401, 422, 429 আর 503-এর জন্য প্রিন্ট করুন।
উত্তর
# 1
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8800/v1/courses/5
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8800/v1/courses/50200
404200 হলো 2xx সাফল্য। 404 হলো 4xx ক্লায়েন্ট এরর: আপনি এমন কিছু চেয়েছেন যা নেই।
# 2
curl -s "http://localhost:8800/v1/echo?dry_run=true" \
-H "X-API-Key: practice-key-123" \
--json '{"course_id": 2, "rating": 5}' | python -m json.tool{
"method": "POST",
"query": {
"dry_run": "true"
},
"headers": {
"user-agent": "curl/8.5.0",
"x-api-key": "practice-key-123",
"content-type": "application/json",
"accept": "application/json"
},
"json": {
"course_id": 2,
"rating": 5
}
}খেয়াল করুন, "dry_run": "true" একটা স্ট্রিং: কোয়েরির মান সবসময় টেক্সট, অথচ বডিতে 2 আর 5 সংখ্যাই রয়ে গেছে।
# 3
def advice(code):
if 200 <= code < 300:
return "success"
if code == 429 or 500 <= code < 600:
return "retry later"
if 400 <= code < 500:
return "fix the request"
if 300 <= code < 400:
return "follow or use cache"
return "unexpected"
for code in (200, 204, 304, 401, 422, 429, 503):
print(code, advice(code))200 success
204 success
304 follow or use cache
401 fix the request
422 fix the request
429 retry later
503 retry laterচেকগুলোর ক্রম গুরুত্বপূর্ণ: সাধারণ 4xx নিয়মের আগে 429 পরীক্ষা করতে হবে, নইলে সেটা "fix the request" দলে পড়ে যেত।
সারসংক্ষেপ
- মেথড বলে কী করতে হবে: GET পড়ে, POST তৈরি করে বা কোনো কাজ করে, PUT পুরোটা বদলায়, PATCH অংশ বদলায়, DELETE মোছে। GET সেফ; GET, PUT আর DELETE আইডেমপোটেন্ট; POST কোনোটাই নয়, তাই সাবধানে আবার পাঠান।
- স্ট্যাটাস পরিবার: 2xx সফল, 3xx অন্য জায়গায় দেখুন, 4xx আপনার রিকোয়েস্ট ঠিক করুন, 5xx সার্ভার ব্যর্থ। 429 আর 5xx অপেক্ষার পরে রিট্রাই করুন; অন্য 4xx না বদলে রিট্রাই করবেন না। এররের বডি সবসময় পড়ুন।
Content-Typeআপনার পাঠানো বডির বর্ণনা দেয়;Acceptবলে আপনি কী ফেরত চান;Authorization/API-কি হেডার ক্রেডেনশিয়াল বহন করে;User-Agentআপনার প্রোগ্রামের নাম বলে। সিক্রেটের জায়গা হেডারে।- JSON-এ আছে অবজেক্ট, অ্যারে, স্ট্রিং, সংখ্যা, বুলিয়ান আর null, যা মেলে dict, list, str, int/float, True/False আর None-এর সাথে। এটা তৈরি করুন
json.dumpsদিয়ে; কখনো হাতে নয়। - পাথ প্যারামিটার একটা রিসোর্স বাছে, কোয়েরি প্যারামিটার একটা কালেকশন ফিল্টার করে বা পাতায় ভাগ করে, আর বডি ডেটা বহন করে। JSON বডির জন্য
curl --json, আর জটিল কোয়েরি মানের জন্য-G --data-urlencodeব্যবহার করুন।
এরপর: ওয়েব API ও REST: ডকুমেন্টেশন পড়া আর curl দিয়ে পরীক্ষা। Practice API ঠিকঠাক সেট আপ করবেন, এর ডকুমেন্টেশন পড়বেন, আর API কি ও বেয়ারার টোকেন ব্যবহার করবেন।