-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Add RestClient.DownloadStreamAsync() error handler #2128
base: dev
Are you sure you want to change the base?
Add RestClient.DownloadStreamAsync() error handler #2128
Conversation
…ror handler to process error responses
Thank you for the PR. Please sign the CLA, and I will merge it. |
@microsoft-github-policy-service agree company="EcuTek Technologies Ltd" |
@alexeyzimarev Sorry for the delay. I followed the instructions of the CLA tool to "sign" the agreement. Is that correct? |
Doesn't seem like it. The check is still queued. |
@dotnet-policy-service agree company="EcuTek Technologies Ltd" |
@alexeyzimarev That worked. I looked at other PRs to see what other people did. It would be nice to mention how to accept the CLA in |
Yeah, it seems like contribution guidelines need updating regardless :) |
@marcoburato-ecutek do you think your PR fixes this one? #1692 |
@alexeyzimarev I don't think so. My change only introduces a new method overload, it does not change previous behaviour. The assumption was that the existing behaviour was correct and intentional. My memory is short, but according to my description above, it was possible to make I reckon the other person should open a separate ticket with a test case, if they believe there's a bug. |
Reopening, I thought this had been merged already. |
There's quite a big issue here as:
|
With regards to the interface, the documentation says
I interpreted that as "IRestClient is deprecated and we only partially brought it back for compatibility". So, I didn't worry about the interface. It seems I misinterpreted the documentation. The About the fact that it only works for downloads, it's already possible to obtain the same error-handling by using IMHO, the best alternative solution would be to use the existing |
The thing with the interface is the whole API scope is available as extension methods for the interface, so they are basically syntactic sugar. It allows creating a new implementation of the interface for whatever reason, and the whole API will work. It was a substantial effort to archive this. I am thinking about a way to accommodate your change in a similar way. I understand the |
I have another suggestion. Why don't we extend interceptors and add |
Maybe. I think that would work for me, as long as the error handler is passed a If you add an What about having a method overload like |
The problem with
I would remove it, rename |
I meant to keep About your proposed changes, that's sensible naming. One limitation of both my original solution and the interceptor is that the error handling would be limited to checking the response and throw a specific exception based on it. That would probably be the common case, but getting a RestResponse as an output would be more flexible. |
Adding stuff to the interface breaks all custom interface implementations as people would need to add this new method there even if they don't use it. |
Another thing is that the error response example you provided when you opened the PR is quite common. It's not only limited to downloads. Often, normal calls return similar results or, for example, problem details. Ultimately, the solution could be that the response deserialisation would support two types. But, indeed, the download case is different because you expect a stream back, but there's nothing there because where's an error. Still, the implementation in this PR would still produce an exception unless you disable I guess the best solution for downloads would be to change the signature of |
Regarding the interface, my understanding is that it's possible to add default implementations to avoid breaking existing consumers. But, I've never used the feature myself and I don't know if it's compatible with old runtimes. Following your idea, I reckon the signature on the interface should be a tuple of |
Any update on this?
I don't like this approach, since I will not be able to read the status code and response headers that often include VERY useful information like file content-type or filename. In Blazor we are now using this new thing called interceptors, but to be honest, right now it is more useful to use raw HttpClient for file downloading scenario, because at least we are getting a HttpResponse easily. In my opinion |
Background
Our API, in case or error, returns a suitable HTTP status code and also provides more detailed information as JSON in the response body. Something like:
For regular requests that send JSON and receive JSON, I could use
RestClient.ExecuteAsync()
to obtain aRestResponse
and deserialise the content as either the expected object or the error object based on the HTTP status code.When downloading files, I have found no good way of doing this. In case of error,
RestClient.DownloadStreamAsync()
would either return null or throw an exception without info about the exact server response ("Request failed with status code XXX").Alternatives considered before adding the new functionality
Using
DownloadStreamAsync()
+RestRequest.OnAfterRequest
This would work in principle. The raw response could be inspected and a suitable application-specific exception could be thrown if it's an error response.
The problem is that the consuming code would have to deal with both RestSharp responses and raw HttpClient responses, which duplicates code. RestSharp also has code to distinguish between a canceled request and a timeout, which would have to be duplicated too.
Using
ExecuteAsync()
+RestRequest.ResponseWriter
This would allow to save the file to a FileStream and get a RestResponse to check the HTTP status code. The obvious problem is that, in case of error, the response error JSON would be saved to the file instead of the expected file content, which is not ideal.
Also, in case of success, RestSharp would still make a byte[] and possibly a string of the response, which is not required and undesired (especially if the file is big).
Using
ExecuteAsync()
+RestRequest.AdvancedResponseWriter
to save the response bodyCompared to the above, this would be passed the raw HTTP response, so it has more control. But this has the same problem as RestRequest.OnAfterRequest (dealing with the raw HTTP response).
Description
This PR adds the following overload:
Task<Stream?> DownloadStreamAsync(RestRequest request, Action<RestResponse> errorHandler, CancellationToken cancellationToken = default)
If the request fails or the server returns an error HTTP status code,
errorHandler
is executed, passing the error response as a RestResponse object.After executing the handler, the method returns null or throws an exception as usual.
Initially, I considered returning a different object which would wrap either the
Stream
or theRestResponse
, but I didn't like the idea of returning a different type on the new overload. I also thought about using anout RestResponse?
argument but they're not allowed on async methods.Example usage:
A single error handler can be defined, to be used for many requests
Purpose
This pull request is a:
Checklist