-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: HttpRequestException w/ Status Code #911
Comments
Thank you for opening this issue. But I thought we discussed this in https://github.com/dotnet/corefx/issues/24253 and decided that having a different exception was not a good pattern. For code that wants to use a similar pattern of HttpWebRequest where non 2xx status codes can have exceptions returned, we encourage the use the HttpClient.EnsureSuccessStatusCode() method: |
I don’t see any discussion of a new exception type in that issue. I can understand encouraging EnsureSuccessStatusCode, however the exception thrown is missing a couple key pieces of data which made WebException in older HttpWebRequest/WebClient-based code so useful / usable. |
@yaakov-h This issue is a dupe of dotnet/corefx#24253 , how is this issue different from that one? |
dotnet/corefx#24253 proposed adding rarely-used fields to an existing exception type. This issue proposes adding a new exception type for this specific purpose. |
Adding a new exception type would be a large change and would break existing API behavior. As I mentioned above, if you need an exception to be thrown for a valid, but unsuccessful, HTTP status code, we recommend using the HttpClient.EnsureSuccessStatusCode() method. |
[EDIT] Reopened later as the alleged dupe is closed. |
Can we re-open dotnet/corefx#24253 then? @davidsh: How would it change existing API behaviour? Furthermore, this entire issue and the linked ones are trying to solve a deficiency in |
Sorry meant to say |
Ooops, I didn't notice dotnet/corefx#24253 is closed. Reopening - we may close/reject it again, but from different reason than being a dupe. |
How would that be a breaking change? The breaking change rules explicitly mention that throwing a more derived exception is allowed. |
I think all that people are asking for is that the status code that is embedded in the HttpRequestException message in EnsureSuccessStatusCode be made available to catchers of the thrown exception without having to parse the message string. Maybe that means extending HttpRequestException with a nullable int or nullable HttpStatusCode property. Maybe that means a whole other derived exception type. Maybe that means using the Exception.Data property. There's a few options, and I don't think folks will be too picky. All they really want is a way to get the response status code without parsing the exception message. Personally, I'd prefer a StatusCode property on the exception to make catch-when filtering nice and clean: try
{
// make call
}
catch (HttpRequestException ex) when (e.StatusCode == 404)
{
// do something
}
catch (HttpRequestException ex) when (e.StatusCode == 503)
{
// do something
} |
I'd like to see this too - preferably as a nullable property on the existing exception type. The |
@davidsh to see comment above since it seems that several folks are interested in this. I agree that a derived exception type is rarely considered breaking, but I don't have context on the issue itself. |
I'm working on an API proposal that will extend HttpRequestException to provide for this StatusCode property as well as another property to handle storing platform-independent error information (similar to |
i cannot believe there is such resistance to exposing the http status code to the calling code. the old WebClient api provided a mechanism for retrieving this. it's amazing to me that the recommended way to find out what caused the exception it to parse the error string - this is insane. |
When creating web crawlers, very often needed to detect what code server returned: HTTP Code 500 or HTTP Code 502 and so on. |
The prior Web APIs like HttpWebRequest would always throw a WebException for any non-successful (non 2xx) status code. And it would throw WebException as well for other network errors. So, it was important and useful that the WebException had HTTP response information (such as StatusCode) in the exception. That was because there may or may not be an actual HTTP protocol response even though the WebException is thrown. In today's HttpClient API, HttpRequestException is not thrown by the HttpClient APIs except for networking errors (like can't connect etc). All HTTP responses containing any StatusCode will be returned. This includes success status codes like 200 or non-success status codes like 500. The discussion is this issue derives from the use of the However, the StatusCode can still be obtained easily from the original HttpResponseMessage returned by the HttpClient APIs prior to any call of This is why it doesn't seem as important to add a StatusCode field to the HttpRequestException because in the majority of cases, there won't be any StatusCode at all (it would have to be a nullable field) since the HttpRequestException thrown by HttpClient APIs typically is due to networking errors where no HTTP response message is ever returned. So, the argument here is whether or not it is a good API design to add a field to the HttpRequestException object where most of the time it will be null. |
@yaakov-h it is on our short list of top backlog asks for .NET Core 3.0 (the milestone reflects that). It has a decent chance to be done in 3.0 (no guarantee though). |
Given @davidsh's summary and insights, I feel more that it is weird to mimic this old style HttpWebRequest-way of surfacing StatusCode to callers from HttpClient via exceptions. I am actually leaning more towards closing it as By Design / Won't Fix. If you are interested in this feature, DESPITE the fact that HttpClient provides a reasonable way to get to StatusCode (even without exceptions being thrown), can you please:
Thanks! (closed by accident - the buttons are too close to each other :(, sorry) |
i don't understand this. You're basically saying that since And then you use that 'argument' (?) as the basis for rejecting this request? I know this is just opinions here, but that reasoning seems specious at best. |
public bool IsSuccessStatusCode
{
get { return ((int)_statusCode >= 200) && ((int)_statusCode <= 299);
}
public HttpResponseMessage EnsureSuccessStatusCode()
{
if (!IsSuccessStatusCode)
{
throw new HttpRequestException(string.Format(
System.Globalization.CultureInfo.InvariantCulture,
SR.net_http_message_not_success_statuscode,
(int)_statusCode,
ReasonPhrase));
}
return this;
} So, the best-practice pattern we recommend when using HttpClient is not to use |
This assumes that you're the direct consumer of the If you are building a library, for example, then:
In all cases, though:
|
@davidsh @karelz This is so very frustrating. You keep saying things to this effect, but as I've already pointed out in this very thread, it's simply not the case.
I don't understand the fascination with performance. For me, the use case is simplicity and readability. I'd say that over 90% of the time when I use The performance overhead of an exception being thrown simply doesn't enter into the equation. |
HttpRequestException.StatusCode propertyRationale and UsageA new nullable HttpRequestException.StatusCode property exposes a response's status code if it has been already received, otherwise it's set to null. This will enable a direct access to the status code on exceptions thrown from HttpClient's simple GET methods and HttpResponseMessage.EnsureSuccessStatusCode(). New HttpRequestException constructors taking a status code will initialize that property. try
{
string response = httpClient.GetStringAsync(requestUri);
...
}
catch(HttpRequestException e)
{
if (e.StatusCode == HttpStatusCode.MovedPermanently)
{
...
}
} Proposed APIpublic class HttpRequestException : Exception
{
...
+ public HttpStatusCode? StatusCode { get; }
public HttpRequestException(string message)
{...}
+ public HttpRequestException(string message, HttpStatusCode? statusCode)
+ {...}
public HttpRequestException(string message, Exception inner)
{...}
+ public HttpRequestException(string message, Exception inner, HttpStatusCode? statusCode)
+ {...}
} @yaakov-h Do you agree with this proposal? If yes, I will update the description. |
@alnikola yep, that does seem to be the simplest way to proceed, I like it. The only question I have on it - should Checking the value would typically require a bit of casting, since everything else under |
Yes, you are right HttpStatusCode would be better. I will update the comment and description. |
public class HttpRequestException : Exception
{
// Existing:
// public HttpRequestException(string message);
// public HttpRequestException(string message, Exception inner);
public HttpRequestException(string message, Exception inner, HttpStatusCode? statusCode);
public HttpStatusCode? StatusCode { get; }
} |
Thanks @terrajobst, @alnikola, and everyone else in getting it through API review. Interesting discussion about re-using HResult, though I do agree the decision to not use that. What are the next steps? Can I just open a Pull Request with these changes + unit tests? |
@yaakov-h Yes, you can now open a PR. |
If you have some questions or need help with something I will be glad to assist. Additionally, if you want to update the documentation by yourself as well, I can show how to do it. If no, I will do it by myself after the PR is completed. |
Thanks. I'll have a look tonight, but my current thinking is:
And then for the docs:
|
Docs:
|
Thanks. I've opened a draft pull request at #32455. If the exception isn't marked as serializable, what (if anything) should I do regarding serialization? There's no Also, what's with updating the docs? Are those in a separate repo? I couldn't find anything relevant under |
To update the docs, you need to go to HttRequestException page and click on "Edit" button at the top-right. It will bring you to dotnet/dotnet-api-docs repo where you can create a PR. Serialization was already discussed on the relevant PR. |
oh gee, that XML looks quite a bit more complicated than I was expecting |
Closed by #32455 |
Latest Proposal
HttpRequestException.StatusCode property
Rationale and Usage
A new nullable HttpRequestException.StatusCode property exposes a response's status code if it has been already received, otherwise it's set to null. This will enable a direct access to the status code on exceptions thrown from HttpClient's simple GET methods and HttpResponseMessage.EnsureSuccessStatusCode(). New HttpRequestException constructors taking a status code will initialize that property.
Proposed API
Original Proposal
Proposed solution to dotnet/corefx#9227, dotnet/corefx#24253 and any related issues:
Linking in SteamRE/SteamKit#517 as well.
I've had the following issue in various codebases:
HttpResponseMessage.EnsureSuccessStatusCode()
In the old .NET HTTP APIs, this was straightforward because
WebException
includes the status code and the response object, where available.Adding
StatusCode
toHttpRequestException
has been rejected in the above-linked issues due to it being of little use in most cases.Would it be possible instead to create a subclass of
HttpRequestException
, and haveEnsureSuccessStatusCode()
throw that instead?Something resembling the following ought to do the trick:
This would achieve the following:
HttpRequestException
does not have a majority-useless field.HttpRequestException
would continue to function as-is.HttpRequestException
.HttpResponseException
, and will then have access to the original HTTP Status Code.Optionally, for consideration, the exception could also include the whole
HttpResponseMessage
object in line with good oldWebException
.The text was updated successfully, but these errors were encountered: