পাঠ ২৪ · ৫৮-এর মধ্যে · মডিউল ৬
Home / Courses / Full-Stack Web Frameworks / HTTP ভার্ব ও স্ট্যাটাস কোড

HTTP ভার্ব ও স্ট্যাটাস কোড সঠিকভাবে ব্যবহার

Using HTTP verbs & status codes correctly
১০ মিনিট পড়া মধ্যবর্তী · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • 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 বার পাঠানো — দুটোর ফলাফল সার্ভারের চূড়ান্ত অবস্থার দিক থেকে অভিন্ন।

GET
Safe: হ্যাঁ · Idempotent: হ্যাঁ — শুধু পড়ে, কতবার কল করলেও একই ডেটা ফেরত দেয়।
POST
Safe: না · Idempotent: না — সাধারণত প্রতিবার নতুন রিসোর্স তৈরি করে (দুইবার কল করলে দুটো তৈরি হতে পারে)।
PUT
Safe: না · Idempotent: হ্যাঁ — সম্পূর্ণ প্রতিস্থাপন করে; একই ডেটা দিয়ে দুইবার কল করলে চূড়ান্ত অবস্থা একই থাকে।
PATCH
Safe: না · Idempotent: গ্যারান্টিযুক্ত নয় — আংশিক আপডেট, প্রয়োগভেদে idempotent হতেও পারে, নাও পারে (যেমন "counter += 1" ধরনের প্যাচ idempotent নয়)।
DELETE
Safe: না · Idempotent: হ্যাঁ — একবার মুছলেই রিসোর্স অনুপস্থিত; দ্বিতীয়বার কল করলেও চূড়ান্ত অবস্থা (অনুপস্থিত) একই থাকে।

২ · কোন অপারেশনে কোন status code

status code শুধু "সফল/ব্যর্থ" বলে না — কী ঘটেছে তা নির্দিষ্টভাবে বলে। ভুল code (যেমন সবকিছুতে 200) ক্লায়েন্টকে ভুল তথ্য দেয়।

201 Created
নতুন রিসোর্স সফলভাবে তৈরি হয়েছে (POST-এর সফল ফলাফল)।
200 OK
সফল পড়া বা আপডেট, বডিতে ডেটা আছে (GET/PUT/PATCH)।
204 No Content
সফল অপারেশন, কিন্তু বডিতে ফেরত দেওয়ার মতো কিছু নেই (সফল DELETE)।
404 Not Found
অনুরোধ করা রিসোর্স অস্তিত্বে নেই।
409 Conflict
রিকোয়েস্ট বর্তমান অবস্থার সাথে সাংঘর্ষিক (যেমন ডুপ্লিকেট রিসোর্স তৈরির চেষ্টা)।

৩ · CRUD ডেটাবেসে সঠিক status code বাস্তবায়ন

নিচের কোড সেলে L23-এর ধাঁচে একটি dict-ভিত্তিক products ডেটাবেস তৈরি হচ্ছে, যার প্রতিটি ফাংশন (create/read/put/patch/delete) সত্যিকারের যুক্তি অনুযায়ী সঠিক status code হিসাব করে রিটার্ন করে — এবং একদম শেষে PUT ও DELETE দুইবার কল করে idempotency যাচাই করা হচ্ছে।

Python
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 অভিন্ন না হলেও চূড়ান্ত অবস্থা অভিন্ন।
মূল কথা · Key takeaway

সঠিক 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 স্পষ্টভাবে বলে দেয় যে অনুরোধ করা আইডি (৯৯৯) কোনো রিসোর্সের সাথে মেলেনি — এটি একটি প্রত্যাশিত, সঠিকভাবে হ্যান্ডল করা অবস্থা, এরর নয়।

অনুশীলন

  1. চিন্তা করুন: একটি ব্যাংকিং API-তে "একটি অ্যাকাউন্টে ১০০ টাকা যোগ করুন" অপারেশনের জন্য POST কেন PUT-এর চেয়ে বেশি নিরাপদ পছন্দ?

    "১০০ টাকা যোগ করুন" একটি relative পরিবর্তন (বর্তমান ব্যালেন্সের উপর নির্ভরশীল), সম্পূর্ণ প্রতিস্থাপন নয়। যদি নেটওয়ার্ক সমস্যায় ক্লায়েন্ট একই PUT balance=1100 রিকোয়েস্ট দুইবার পাঠায় (idempotent ধরে নিয়ে), কিন্তু আসলে সার্ভার প্রথমবারই সফলভাবে প্রসেস করেছিল আর সেই সময় ব্যালেন্স অন্য কোনো লেনদেনে পরিবর্তন হয়েছিল, তাহলে ভুল চূড়ান্ত মান বসে যেতে পারে। POST ব্যবহার করে প্রতিটি "টাকা যোগ করার" রিকোয়েস্টকে একটি নতুন, ট্র্যাক-করা লেনদেন হিসেবে গণ্য করা নিরাপদ, কারণ POST idempotent নয় বলে ডেভেলপার সচেতনভাবে ডুপ্লিকেট-প্রতিরোধ লজিক (এই পাঠের ৪০৯ চেকের মতো) যোগ করতে বাধ্য হন।

  2. পরীক্ষা করুন: উপরের কোড সেলে 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 — সব এক জায়গায়।
আগের পাঠ
REST রিসোর্স মডেলিং