Ik ben bezig met een web API geschreven in .NET Core (MVC), die communiceert via JSON. Op de client (.NET Windows, WPF) gebruik ik RestSharp om de verbinding te maken.
Elke functie op de server die de client kan aanroepen laat ik een instance van ApiResponse terug sturen, welke een simpele wrapper is die naast de "payload" (object) ook een error message terug kan geven.
Ik stuur dus altijd een "OK" terug.
Op de client kan ik nu checken:
- Als de http code "OK" is dan weet ik dat ik een ApiResponse terug zal krijgen, dus kan ik die casten (of gebruik maken van de RestSharp 'Execute<T>' generic functies). Daarna kan ik specifiek checken of "success == true" en de payload gebruiken. Als "success == false" weet ik dat ik een error message kan verwachten.
- Als de http code niet "OK" is dan is er iets ernstig fout en kan ik ook geen nuttige error geven behalve dat er iets mis is met de server.
Nu heb ik een functie waarmee ik de client een bestand wil laten downloaden. Het bestand staat op de server (zip bestand) en kan mogelijk tot ~1 GB groot zijn (momenteel nog lang niet, maar dit moet flexibel zijn voor grotere bestanden later). Op het moment gebruik ik hier heel simpel een "payload" van een byte array voor, oftewel de server stuurt gewoon de raw bytes in een keer.
Ik ben bang dat dit niet goed schaalt en weet dat ik eigenlijk gebruik zou moeten maken van file streams. In de web API op de server kan ik daarvoor bijvoorbeeld een "FileResult" terug sturen, bijvoorbeeld:
Op de client zou ik dan gebruik moeten maken van de RestSharp DownloadData functie en de ResponseWriter op ongeveer zo'n manier:
Ik heb nu een aantal vragen:
1. In het geval van de filestream methode krijg ik als response alsnog gewoon een byte array terug. Het probleem is dat ik hier dus geen status code of niks uit krijg. Hoe weet ik of er iets mis is gegaan en welke error ik moet tonen?
2. Ik kan nu dus geen ApiResponse maken en terug sturen, laat staan zelf errors terugsturen. Op de server gebeuren een groot aantal checks en als ergens niet aan voldaan is (bijv gebruiker mag deze file niet hebben) dan wil ik een error terug geven die logisch is, en niet gewoon een "404" of whatever. Nu weet ik dat er speciale error codes zijn voor "not authenticated" etc maar die zijn lang niet toereikend voor mijn geval. Ik wil gewoon een eigen error message vanuit de server kunnen terugsturen.
Wat ik dus eigenlijk wil is alsnog een ApiResponse terug sturen, zodat ik er zelf een error aan kan knopen, maar alsnog de file niet in een ruk maar als een filestream kan behandelen.
Heeft iemand een idee?
Elke functie op de server die de client kan aanroepen laat ik een instance van ApiResponse terug sturen, welke een simpele wrapper is die naast de "payload" (object) ook een error message terug kan geven.
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| [HttpGet] public async Task<IActionResult> GetSomething() { var something = await _repo.GetSomethingAsync(); ApiResponse response; if (something == null) response = new ApiResponse(success: false, error: "Something went wrong"); else response = new ApiResponse(success: true, payload: something); return Ok(response); } |
Ik stuur dus altijd een "OK" terug.
Op de client kan ik nu checken:
- Als de http code "OK" is dan weet ik dat ik een ApiResponse terug zal krijgen, dus kan ik die casten (of gebruik maken van de RestSharp 'Execute<T>' generic functies). Daarna kan ik specifiek checken of "success == true" en de payload gebruiken. Als "success == false" weet ik dat ik een error message kan verwachten.
- Als de http code niet "OK" is dan is er iets ernstig fout en kan ik ook geen nuttige error geven behalve dat er iets mis is met de server.
Nu heb ik een functie waarmee ik de client een bestand wil laten downloaden. Het bestand staat op de server (zip bestand) en kan mogelijk tot ~1 GB groot zijn (momenteel nog lang niet, maar dit moet flexibel zijn voor grotere bestanden later). Op het moment gebruik ik hier heel simpel een "payload" van een byte array voor, oftewel de server stuurt gewoon de raw bytes in een keer.
C#:
1
2
3
4
5
6
7
8
9
10
11
| // Client: var request = CreateRequest("api/get_file"); var response = await _client.ExecuteTaskAsync<ApiResponse<byte[]>>(request); if (response.StatusCode == HttpStatusCode.OK) { var apiResponse = response.Data; if (apiResponse.Success) { return apiResponse.Payload; // file contents as byte[] } } |
Ik ben bang dat dit niet goed schaalt en weet dat ik eigenlijk gebruik zou moeten maken van file streams. In de web API op de server kan ik daarvoor bijvoorbeeld een "FileResult" terug sturen, bijvoorbeeld:
C#:
1
2
3
4
5
6
7
8
| [HttpGet] public async Task<IActionResult> GetFile() { using (var stream = await _repo.GetStream()) { return File(stream, "application/octet-stream"); } } |
Op de client zou ik dan gebruik moeten maken van de RestSharp DownloadData functie en de ResponseWriter op ongeveer zo'n manier:
C#:
1
2
3
4
5
6
7
8
| // Client: using (var stream = File.Open("test.zip")) { var request = CreateRequest("api/get_file"); request.ResponseWriter = s => s.CopyTo(stream); var response = _client.DownloadData(request); // byte[] } |
Ik heb nu een aantal vragen:
1. In het geval van de filestream methode krijg ik als response alsnog gewoon een byte array terug. Het probleem is dat ik hier dus geen status code of niks uit krijg. Hoe weet ik of er iets mis is gegaan en welke error ik moet tonen?
2. Ik kan nu dus geen ApiResponse maken en terug sturen, laat staan zelf errors terugsturen. Op de server gebeuren een groot aantal checks en als ergens niet aan voldaan is (bijv gebruiker mag deze file niet hebben) dan wil ik een error terug geven die logisch is, en niet gewoon een "404" of whatever. Nu weet ik dat er speciale error codes zijn voor "not authenticated" etc maar die zijn lang niet toereikend voor mijn geval. Ik wil gewoon een eigen error message vanuit de server kunnen terugsturen.
Wat ik dus eigenlijk wil is alsnog een ApiResponse terug sturen, zodat ik er zelf een error aan kan knopen, maar alsnog de file niet in een ruk maar als een filestream kan behandelen.
Heeft iemand een idee?