Runtime: Proposal: Add RawStatusCode to HttpResponseMessage

Created on 14 Aug 2019  路  5Comments  路  Source: dotnet/runtime

HttpResponseMessage.StatusCode is currently an HttpStatusCode enum. This creates a little bit of friction when using HttpClient to deal with HTTP status codes not covered by this enum, because you have to cast the enum to an int. This is also the case if you are using libraries that type status codes as integers, e.g. if you are comparing a response code to one of the StatusCodes values in ASP.NET Core.

This is especially relevant to ASP.NET Core, because the entirety of their ecosystem is predicated on "HTTP status codes are integers", and using HttpClient (and therefore HttpReponseMessage) in ASP.NET Core applications is an extremely common scenario.

Proposal

  • Add a RawStatusCode property to HttpResponseMessage that contains the actual integer status code as returned by the client
  • Make the current StatusCode property read and cast that property's value

Psuedocode:

class HttpResponseMessage
{
    int RawStatusCode { get; }

    HttpStatusCode StatusCode => (HttpStatusCode) RawStatusCode;
}

Secondary proposal

Looking at the HttpResponseMessage source, it does some casting of StatusCode to int. This casting would not be necessary if corefx had a class similar to ASP.NET Core's aforementioned StatusCodes.

In fact, for the reasons I outlined above, having such a class that is delegated to by the HttpStatusCode enum would IMO be an ideal solution, because it would (a) allow range checks such as those done in HttpResponseMessage to use that class instead of hardcoding (b) would allow people who are more familiar with status codes as integers to use that class with the proposed RawStatusCode property (i.e. they would never need to touch/worry about HttpStatusCode).

E.g.:

class KnownHttpStatusCode // naming, the second hard problem in computer science
{
    public const int OK = 200;
    ...
    public const int NetworkAuthenticationRequired = 511;
}

enum HttpStatusCode
{
    OK = KnownHttpStatusCode.OK,
    ...
    NetworkAuthenticationRequired = KnownHttpStatusCode.NetworkAuthenticationRequired,
}
api-suggestion area-System.Net.Http

Most helpful comment

This creates a little bit of friction when using HttpClient to deal with HTTP status codes not covered by this enum, because you have to cast the enum to an int.

Why is it a source of friction? For what it's worth, one possible alternative approach would be to augment the enum with missing status codes, something that was actually done recently.

When using older versions of dotnet, you can typically deal with missing status codes in application code as follows:

    public static class HttpStatusCodeExt
    {
        public const HttpStatusCode ImATeapot = (HttpStatusCode) 418;
    }

You can even use this in attributes and pattern matches.

All 5 comments

This creates a little bit of friction when using HttpClient to deal with HTTP status codes not covered by this enum, because you have to cast the enum to an int.

Why is it a source of friction? For what it's worth, one possible alternative approach would be to augment the enum with missing status codes, something that was actually done recently.

When using older versions of dotnet, you can typically deal with missing status codes in application code as follows:

    public static class HttpStatusCodeExt
    {
        public const HttpStatusCode ImATeapot = (HttpStatusCode) 418;
    }

You can even use this in attributes and pattern matches.

@stephentoub

@eiriktsarpalis What you're suggesting is that I declare an additional member, simply for the purpose of avoiding casting. Or to put it more succinctly, I get to swap the current code smell (casting) for another smell (duplication). Can you tell I'm not thrilled at the prospect?

I appreciate that getting a new member added to any class is not an insignificant thing; I appreciate that adding one for an apparently specious reason like "I don't like casting" is not going to be the highest priority on anyone's radar. But I also believe that adding this property would be a minor change that would make a small but significant improvement to the quality-of-life experience for .NET developers, particularly those used to working with integer status codes.

p.s. thanks for the link to that PR, I've actually been looking for the source of HttpStatusCodes - in Framework reference source it's in its own file, didn't realise that corefx has glommed it into a single big file along with a whole bunch of other stuff.

Same problem here. And I end up adding an extension method to get the int status code value,

using static System.Net.HttpStatusCode;

internal static int ToValue(this HttpStatusCode status)
{
            return status switch
            {
                Continue => 100,
                SwitchingProtocols => 101,
                Processing => 102,
                EarlyHints => 103,
                OK => 200,
                Created => 201,
                Accepted => 202,
                NonAuthoritativeInformation => 203,
                NoContent => 204,
                ResetContent => 205,
                PartialContent => 206,
                MultiStatus => 207,
                AlreadyReported => 208,
                IMUsed => 226,
                MultipleChoices => 300,
                Moved => 301,
                Redirect => 302,
                SeeOther => 303,
                NotModified => 304,
                UseProxy => 305,
                Unused => 306,
                TemporaryRedirect => 307,
                PermanentRedirect => 308,
                BadRequest => 400,
                Unauthorized => 401,
                PaymentRequired => 402,
                Forbidden => 403,
                NotFound => 404,
                MethodNotAllowed => 405,
                NotAcceptable => 406,
                ProxyAuthenticationRequired => 407,
                RequestTimeout => 408,
                Conflict => 409,
                Gone => 410,
                LengthRequired => 411,
                PreconditionFailed => 412,
                RequestEntityTooLarge => 413,
                RequestUriTooLong => 414,
                UnsupportedMediaType => 415,
                RequestedRangeNotSatisfiable => 416,
                ExpectationFailed => 417,
                MisdirectedRequest => 421,
                UnprocessableEntity => 422,
                Locked => 423,
                FailedDependency => 424,
                UpgradeRequired => 426,
                PreconditionRequired => 428,
                TooManyRequests => 429,
                RequestHeaderFieldsTooLarge => 431,
                UnavailableForLegalReasons => 451,
                InternalServerError => 500,
                NotImplemented => 501,
                BadGateway => 502,
                ServiceUnavailable => 503,
                GatewayTimeout => 504,
                HttpVersionNotSupported => 505,
                VariantAlsoNegotiates => 506,
                InsufficientStorage => 507,
                LoopDetected => 508,
                NotExtended => 510,
                NetworkAuthenticationRequired => 511,
                _ => default
           };
    }

Triage: We don't see value of this. You can cast HttpStatusCode to int today and it should just work.

Was this page helpful?
0 / 5 - 0 ratings