Abbiamo iniziato a lavorare su un'app mobile utilizzando Xamarin. C'è una libreria di base che deve essere condivisa tra le app Android e iOS. Nella libreria principale ci saranno diverse funzioni per comunicare con un servizio web. Ho avuto una discussione con mio fratello su quale sarebbe l'approccio migliore, entrambi abbiamo un'opinione diversa, quindi vorremmo sentire le vostre opinioni in merito. E forse c'è un approccio ancora migliore a cui entrambi non avevamo ancora pensato.
Il servizio web con cui parliamo ha alcuni codici di stato HTTP predefiniti per determinati errori. Ad esempio, quando si esegue la verifica di un account e le informazioni non sono corrette, il servizio Web potrebbe restituire un codice di stato 400 e alcuni altri errori potrebbero comportare un codice di stato diverso.
Il mio approccio era il seguente:
public async Task<string> VerifyAccountAsync (string email, string password) {
// prevent null values
email = email ?? "";
password = password ?? "";
var queryString = String.Format ("/VerifyAccount?email={0}&password={1}", email, password);
var request = WebRequest.Create (_accountUrlString + queryString);
string result = null;
try {
using (var response = await request.GetResponseAsync ()) {
using (var stream = response.GetResponseStream ()) {
using (var reader = new StreamReader (stream)) {
result = reader.ReadToEnd ();
}
}
}
} catch (WebException ex) {
// a protocol exception means we got a response, but the status code implies some error occured.
if (ex.Status == WebExceptionStatus.ProtocolError) {
var httpResponse = (HttpWebResponse)ex.Response;
switch (httpResponse.StatusCode) {
case HttpStatusCode.BadRequest: // 400
throw new DepotServiceException (httpResponse, "User could not be found.");
case HttpStatusCode.InternalServerError: // 500
throw new DepotServiceException (httpResponse, "Some error");
default: // TODO: use the response stream to set the error message.
throw new DepotServiceException (httpResponse, "Undefined error ...");
}
} else {
// some other exception occured, we'll just rethrow it (should not be ignored)
throw ex;
}
}
return result;
}
Il mio ragionamento era il seguente:
- Poiché il servizio Web utilizza alcuni codici di stato predefiniti per determinati errori, dovremmo utilizzare un'eccezione tipizzata (DepotServiceException) per rendere più chiaro per il consumatore della libreria principale qual è l'origine dell'errore.
- Gestire le eccezioni in questo modo nella libreria principale, impedirà di dover utilizzare le costruzioni if / else nei livelli visivi iPhone / Android. Nel livello visivo non dovremo controllare quale fosse il codice di stato, per mostrare il messaggio di errore appropriato.
L'approccio di mio fratello è il seguente:
public async Task<DepotServiceResult> RetrieveUrl(string url) {
DepotServiceResult serviceResult = new DepotServiceResult();
WebRequest request = WebRequest.Create(url);
try {
using (var response = await request.GetResponseAsync()) {
var webresponse = (HttpWebResponse)response;
using (var stream = response.GetResponseStream()) {
serviceResult.statuscode = webresponse.StatusCode;
using (var streamReader = new StreamReader(stream)) {
serviceResult.response = streamReader.ReadToEnd();
}
}
}
} catch (WebException ex) {
serviceResult.errorDescription = ex.ToString();
}
return serviceResult;
}
public async Task<DepotServiceResult> VerifyAccountAsync(string email, string password) {
var url = _urlBase + "api/account/VerifyAccount?email={0}&password={1}";
email = WebUtility.HtmlEncode (email);
password = WebUtility.HtmlEncode (password);
url = String.Format (url, email, password);
return await RetrieveUrl(url);
}
public class DepotServiceResult {
public string errorDescription;
public HttpStatusCode statuscode;
public string response;
}
Mio fratello preferisce il suo approccio perché non vuole che il livello principale della app maneggi l'errore. Preferisce gestire l'errore nel livello dell'interfaccia utente dell'app. Pertanto, per il suo approccio, ha scelto di creare un oggetto DepotServiceResult
che contenga l'intero risultato dell'operazione, inclusi eventuali errori in caso di errore.
Personalmente, credo che il mio approccio sia il migliore, anche se ho difficoltà a spiegare perché è l'approccio migliore. Quindi, se qualcuno fosse d'accordo con me, potrei avere delle buone ragioni sul perché è l'approccio migliore? Certo, mio fratello preferisce il suo approccio e se il suo approccio è migliore, mi piacerebbe sicuramente sentire un buon ragionamento sul perché è il migliore.
Se c'è un approccio ancora migliore rispetto alle nostre preferenze, ci piacerebbe sentirlo anche.