HTTP ভার্ব ও স্ট্যাটাস কোড সঠিকভাবে ব্যবহার
এই পাঠে যা শিখবেন
- GET/POST/PUT/PATCH/DELETE-এর অর্থ এবং safe/idempotent বৈশিষ্ট্য কী বোঝায়
- কোন অপারেশনে কোন status code সঠিক — 201, 200, 204, 404, 409
- একটি বাস্তব dict-ভিত্তিক CRUD ডেটাবেসে প্রতিটি verb প্রয়োগ করে সঠিক code পাওয়া যাচাই করা
PUTওDELETEদুইবার কল করে idempotency বাস্তবে প্রমাণ করা
১ · Safe ও Idempotent — দুটো আলাদা বৈশিষ্ট্য
HTTP মেথড বোঝার জন্য দুটো গুরুত্বপূর্ণ ধারণা আলাদা করে চেনা দরকার। Safe মানে মেথডটি সার্ভারের ডেটা পরিবর্তন করে না (শুধু পড়ে)। Idempotent মানে একই রিকোয়েস্ট একবার পাঠানো আর N বার পাঠানো — দুটোর ফলাফল সার্ভারের চূড়ান্ত অবস্থার দিক থেকে অভিন্ন।
Safe: হ্যাঁ · Idempotent: হ্যাঁ — শুধু পড়ে, কতবার কল করলেও একই ডেটা ফেরত দেয়।
Safe: না · Idempotent: না — সাধারণত প্রতিবার নতুন রিসোর্স তৈরি করে (দুইবার কল করলে দুটো তৈরি হতে পারে)।
Safe: না · Idempotent: হ্যাঁ — সম্পূর্ণ প্রতিস্থাপন করে; একই ডেটা দিয়ে দুইবার কল করলে চূড়ান্ত অবস্থা একই থাকে।
Safe: না · Idempotent: গ্যারান্টিযুক্ত নয় — আংশিক আপডেট, প্রয়োগভেদে idempotent হতেও পারে, নাও পারে (যেমন "counter += 1" ধরনের প্যাচ idempotent নয়)।
Safe: না · Idempotent: হ্যাঁ — একবার মুছলেই রিসোর্স অনুপস্থিত; দ্বিতীয়বার কল করলেও চূড়ান্ত অবস্থা (অনুপস্থিত) একই থাকে।
২ · কোন অপারেশনে কোন status code
status code শুধু "সফল/ব্যর্থ" বলে না — কী ঘটেছে তা নির্দিষ্টভাবে বলে। ভুল code (যেমন সবকিছুতে
200) ক্লায়েন্টকে ভুল তথ্য দেয়।
নতুন রিসোর্স সফলভাবে তৈরি হয়েছে (
POST-এর সফল ফলাফল)।সফল পড়া বা আপডেট, বডিতে ডেটা আছে (
GET/PUT/PATCH)।সফল অপারেশন, কিন্তু বডিতে ফেরত দেওয়ার মতো কিছু নেই (সফল
DELETE)।অনুরোধ করা রিসোর্স অস্তিত্বে নেই।
রিকোয়েস্ট বর্তমান অবস্থার সাথে সাংঘর্ষিক (যেমন ডুপ্লিকেট রিসোর্স তৈরির চেষ্টা)।
৩ · CRUD ডেটাবেসে সঠিক status code বাস্তবায়ন
নিচের কোড সেলে L23-এর ধাঁচে একটি dict-ভিত্তিক products ডেটাবেস তৈরি হচ্ছে, যার প্রতিটি ফাংশন
(create/read/put/patch/delete) সত্যিকারের
যুক্তি অনুযায়ী সঠিক status code হিসাব করে রিটার্ন করে — এবং একদম শেষে PUT ও DELETE
দুইবার কল করে idempotency যাচাই করা হচ্ছে।
products = {}
next_product_id = 1
def create_product(data):
"""POST -- 201 তৈরি হলে, 409 ডুপ্লিকেট শিরোনাম হলে"""
global next_product_id
for p in products.values():
if p["title"] == data["title"]:
return 409, {"error": f"'{data['title']}' শিরোনামে প্রোডাক্ট ইতিমধ্যে আছে"}
pid = next_product_id
next_product_id += 1
products[pid] = {"id": pid, "title": data["title"], "price": data["price"]}
return 201, products[pid]
def read_product(pid):
"""GET -- 200 পাওয়া গেলে, 404 না পেলে"""
if pid in products:
return 200, products[pid]
return 404, {"error": "প্রোডাক্ট পাওয়া যায়নি"}
def put_product(pid, data):
"""PUT -- সম্পূর্ণ প্রতিস্থাপন, idempotent -- 200 আপডেট হলে, 404 না থাকলে"""
if pid not in products:
return 404, {"error": "প্রোডাক্ট পাওয়া যায়নি"}
products[pid] = {"id": pid, "title": data["title"], "price": data["price"]}
return 200, products[pid]
def patch_product(pid, partial):
"""PATCH -- আংশিক আপডেট -- 200 আপডেট হলে, 404 না থাকলে"""
if pid not in products:
return 404, {"error": "প্রোডাক্ট পাওয়া যায়নি"}
products[pid].update(partial)
return 200, products[pid]
def delete_product(pid):
"""DELETE -- idempotent -- 204 মোছা হলে, 404 আগেই না থাকলে (কিন্তু crash হয় না)"""
if pid in products:
del products[pid]
return 204, None
return 404, {"error": "প্রোডাক্ট পাওয়া যায়নি (আগেই মোছা হয়েছে)"}
print("== POST (তৈরি) ==")
s1, b1 = create_product({"title": "ল্যাপটপ", "price": 50000})
print(f"create #1 -> {s1} {b1}")
s2, b2 = create_product({"title": "ল্যাপটপ", "price": 52000})
print(f"create #2 (একই শিরোনাম) -> {s2} {b2}")
print("\n== GET (পড়া) ==")
s3, b3 = read_product(1)
print(f"read(1) -> {s3} {b3}")
s4, b4 = read_product(999)
print(f"read(999) -> {s4} {b4}")
print("\n== PUT (সম্পূর্ণ প্রতিস্থাপন, idempotent) ==")
s5, b5 = put_product(1, {"title": "ল্যাপটপ", "price": 48000})
print(f"put #1 -> {s5} {b5}")
s6, b6 = put_product(1, {"title": "ল্যাপটপ", "price": 48000})
print(f"put #2 (হুবহু একই ডেটা) -> {s6} {b6}")
print(f"যাচাই -- দুইবার PUT-এর ফলাফল অবস্থা অভিন্ন: {b5 == b6}")
print("\n== PATCH (আংশিক আপডেট) ==")
s7, b7 = patch_product(1, {"price": 47000})
print(f"patch(1, price=47000) -> {s7} {b7}")
print("\n== DELETE (মোছা, idempotent) ==")
s8, b8 = delete_product(1)
print(f"delete #1 -> {s8} {b8}")
s9, b9 = delete_product(1)
print(f"delete #2 (আগেই মোছা) -> {s9} {b9}")
print(f"যাচাই -- দ্বিতীয় DELETE-এও এরর/ক্র্যাশ হয়নি, শুধু 404; রিসোর্স এখনো অনুপস্থিত: {1 not in products}")
create_product কল করলে একই শিরোনাম আগে থেকেই আছে বলে 409 Conflict আসে —
নতুন প্রোডাক্ট তৈরি হয় না। put_product দুইবার হুবহু একই ডেটা দিয়ে কল করলে দুইবারই
200 এবং দুইবারই ফলাফল (b5 == b6) অভিন্ন — এটাই idempotency। delete_product
প্রথমবার 204 দেয় (মোছা হলো), দ্বিতীয়বার রিসোর্স আর নেই বলে 404 দেয় — কিন্তু কোনো এরর
ছোড়া হয়নি এবং প্রোডাক্ট ১ এর অবস্থা (অনুপস্থিত) দুইবারের পরও অভিন্ন — এটিই DELETE-এর idempotency, status code
অভিন্ন না হলেও চূড়ান্ত অবস্থা অভিন্ন।
সঠিক status code মানে ক্লায়েন্টকে ঠিক কী ঘটেছে তা নির্ভুলভাবে জানানো — এবং idempotency মানে কোনো নেটওয়ার্ক
সমস্যায় ক্লায়েন্ট নিরাপদে একই PUT/DELETE রিকোয়েস্ট আবার পাঠাতে পারে, চূড়ান্ত
অবস্থা ভুল হয়ে যাওয়ার ভয় ছাড়াই — যা POST-এর ক্ষেত্রে করা যায় না (তাই L23-এর মতো ডুপ্লিকেট-চেক
গুরুত্বপূর্ণ)।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
দ্বিতীয়বার delete_product(1) কল করলে 404 কেন আসে, 204 নয়?
কারণ প্রথম কলেই প্রোডাক্ট ১ মুছে ফেলা হয়েছে — দ্বিতীয়বার 1 in products চেক করলে
False পাওয়া যায়, তাই ফাংশন "রিসোর্স নেই" ধরে 404 রিটার্ন করে। এটি এখনো
idempotent, কারণ দুইবার কল করার পরও চূড়ান্ত অবস্থা একই (প্রোডাক্ট অনুপস্থিত) — idempotency মানে
status code সবসময় একই হবে তা নয়, বরং সার্ভারের অবস্থা একই থাকবে।
প্র ০২
PATCH-কে "idempotent গ্যারান্টিযুক্ত নয়" বলা হলো কেন, যেখানে এই কোডে patch_product আসলে idempotent আচরণ করছে?
এই নির্দিষ্ট উদাহরণে patch_product(1, {"price": 47000}) সবসময় price-কে একই মান
(47000) সেট করে, তাই বারবার কল করলেও ফলাফল একই — এই বিশেষ ক্ষেত্রে এটি idempotent।
কিন্তু PATCH-এর সংজ্ঞা অনুযায়ী এটি গ্যারান্টিযুক্ত নয় — যদি প্যাচ ডেটা এমন হতো যা মানকে
সেট না করে পরিবর্তন করে (যেমন {"price_delta": -1000} দিয়ে প্রতিবার
দাম আরও ১০০০ কমানো), তাহলে দুইবার কল করলে প্রথমবারের চেয়ে ভিন্ন চূড়ান্ত মূল্য পাওয়া যেত — তখন এটি
আর idempotent থাকত না।
প্র ০৩
কেন read_product(999)-এর জন্য 404 ব্যবহার করা হলো, 200 খালি বডি নিয়ে নয়?
কারণ 200 মানে "রিকোয়েস্ট সফল, রিসোর্স পাওয়া গেছে" — খালি বডি দিয়ে 200 ফেরত দিলে
ক্লায়েন্ট বিভ্রান্ত হতো (রিসোর্স আছে কিন্তু খালি, নাকি আদৌ নেই?)। 404 স্পষ্টভাবে বলে দেয় যে
অনুরোধ করা আইডি (৯৯৯) কোনো রিসোর্সের সাথে মেলেনি — এটি একটি প্রত্যাশিত, সঠিকভাবে হ্যান্ডল করা অবস্থা, এরর
নয়।
অনুশীলন
-
চিন্তা করুন: একটি ব্যাংকিং API-তে "একটি অ্যাকাউন্টে ১০০ টাকা যোগ করুন" অপারেশনের জন্য
POSTকেনPUT-এর চেয়ে বেশি নিরাপদ পছন্দ?"১০০ টাকা যোগ করুন" একটি relative পরিবর্তন (বর্তমান ব্যালেন্সের উপর নির্ভরশীল), সম্পূর্ণ প্রতিস্থাপন নয়। যদি নেটওয়ার্ক সমস্যায় ক্লায়েন্ট একই
PUT balance=1100রিকোয়েস্ট দুইবার পাঠায় (idempotent ধরে নিয়ে), কিন্তু আসলে সার্ভার প্রথমবারই সফলভাবে প্রসেস করেছিল আর সেই সময় ব্যালেন্স অন্য কোনো লেনদেনে পরিবর্তন হয়েছিল, তাহলে ভুল চূড়ান্ত মান বসে যেতে পারে।POSTব্যবহার করে প্রতিটি "টাকা যোগ করার" রিকোয়েস্টকে একটি নতুন, ট্র্যাক-করা লেনদেন হিসেবে গণ্য করা নিরাপদ, কারণPOSTidempotent নয় বলে ডেভেলপার সচেতনভাবে ডুপ্লিকেট-প্রতিরোধ লজিক (এই পাঠের ৪০৯ চেকের মতো) যোগ করতে বাধ্য হন। -
পরীক্ষা করুন: উপরের কোড সেলে
create_product({"title": "মাউস", "price": 800})দিয়ে একটি নতুন কল যোগ করুন এবং এর status code ও বডি প্রিন্ট করুন — এটি কি201দেয়, নাকি409?এটি
201 Createdদেবে, কারণ "মাউস" শিরোনামটিproducts-এ আগে থেকে নেই (শুধু "ল্যাপটপ" ছিল, যা ইতিমধ্যেdelete_product-এর মাধ্যমে মোছাও হয়ে গেছে) — তাই ডুপ্লিকেট-চেক লুপ কোনো মিল খুঁজে পায় না এবং একটি নতুনidনিয়ে প্রোডাক্টটি তৈরি হয়।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ আর্কিটেকচার প্যাটার্ন, ফ্রন্ট-এন্ড/ব্যাক-এন্ড ফ্রেমওয়ার্ক ফান্ডামেন্টাল, স্টেট ম্যানেজমেন্ট, REST API, ORM, অথেন্টিকেশন, রেন্ডারিং স্ট্র্যাটেজি ও ডিপ্লয়মেন্ট — বাকি পাঠগুলো শীঘ্রই যুক্ত হবে।
- পূর্ববর্তী পাঠ: REST রিসোর্স মডেলিং L23 এই পাঠের CRUD ডেটাবেসটি L23-এর নেস্টেড রিসোর্স মডেলিং ধারণার উপর সরাসরি গড়ে উঠেছে।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git, Theory of Computation, Engineering Economics ও Full-Stack Web Frameworks — সব এক জায়গায়।