HttpStatusCode
success
failure
HTTP
status codes

Checking if HttpStatusCode represents success or failure

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

HTTP status evaluation is simple in principle but often implemented inconsistently. Many codebases check only 200 OK and treat everything else as failure, which breaks valid responses like 201 Created, 204 No Content, or 304 Not Modified depending on client semantics. The robust way is to evaluate by status class and business intent.

In most APIs, 2xx indicates success, 4xx indicates client-side problems, and 5xx indicates server-side failures. But different endpoints can intentionally use specific non-2xx flows, so status checks should be explicit and testable.

Core Sections

1. Classify by status family

In .NET, you can cast to int and check range:

csharp
1using System.Net;
2
3bool IsSuccess(HttpStatusCode code)
4{
5    int n = (int)code;
6    return n >= 200 && n <= 299;
7}

Equivalent with HttpResponseMessage:

csharp
1if (response.IsSuccessStatusCode)
2{
3    // success path
4}

IsSuccessStatusCode already applies the 2xx rule.

2. Handle expected exceptions to the general rule

Some workflows treat 404 as acceptable “not found” signals, or 409 as conflict to retry differently.

csharp
1switch (response.StatusCode)
2{
3    case HttpStatusCode.OK:
4    case HttpStatusCode.Created:
5    case HttpStatusCode.NoContent:
6        return Result.Success();
7    case HttpStatusCode.NotFound:
8        return Result.Empty();
9    default:
10        return Result.Fail($"HTTP {(int)response.StatusCode}");
11}

Make these exceptions explicit rather than buried in generic error handling.

3. Preserve response body for diagnostics

Failure classification without body context is often insufficient.

csharp
1if (!response.IsSuccessStatusCode)
2{
3    string body = await response.Content.ReadAsStringAsync();
4    _logger.LogWarning("Request failed: {Status} {Body}", response.StatusCode, body);
5}

This shortens debugging cycles for validation and auth failures.

4. Beware redirects and transport-level errors

3xx may be auto-followed by client handlers. Also, networking failures (DNS timeout, TLS errors) do not produce HTTP status at all. Handle HttpRequestException separately.

5. Centralize policy in one utility

Create one status classifier used across all HTTP clients to avoid inconsistent behavior across services.

Common Pitfalls

  • Treating only 200 as success and mishandling valid 201/204 responses.
  • Ignoring endpoint-specific semantics where certain 4xx statuses are expected outcomes.
  • Dropping error response bodies and losing essential diagnostic details.
  • Confusing transport exceptions with HTTP error statuses.
  • Duplicating ad hoc status logic across services without a shared utility.

Summary

Checking HTTP success or failure should be based on explicit policy, not hardcoded single-status assumptions. Default to 2xx as success, then layer endpoint-specific rules for expected alternatives. Log failure payloads for visibility and keep transport exceptions distinct from HTTP responses. Centralized, consistent status handling makes client behavior predictable and easier to maintain.

A practical way to keep this issue solved is to convert the guidance into a repeatable runbook that can be executed by anyone on the team. Write down the exact environment assumptions, dependency versions, runtime flags, and validation commands required to confirm the behavior. Include expected outputs for the happy path and one or two known failure signatures so the next engineer can quickly classify what they are seeing. This turns fragile tribal knowledge into an operational artifact that survives handoffs, on-call rotations, and context switches.

It is also useful to add one lightweight automated guardrail in CI so regressions are caught before deployment. The guardrail should target the most failure-prone step in the workflow: an import smoke test, configuration lint, compatibility check, integration probe, or small benchmark assertion. Keep that check fast enough to run on every change and explicit enough that failure messages are actionable. In teams with parallel contributors, early automated detection prevents repeated debugging of the same class of issue.

Finally, keep examples current as tools and frameworks evolve. A command or API that worked six months ago may become deprecated, renamed, or behaviorally different. Treat documentation updates as normal maintenance work, just like test upkeep. When guidance is version-aware and tested regularly, you avoid drift between article recommendations and production reality, and the content remains useful for both new and experienced engineers.


Course illustration
Course illustration

All Rights Reserved.