I am currently trying to develop simple web application for my IoT school project. As for now it should only call direct methods from my Raspberry.
I am using Azure SDK for C#.
This is how code looks like:
Controller:
public ActionResult changeState(int? id, bool enable)
{
string conn_str = (from u in db.Users join h in db.Hubs on
u.Hub.HubId equals h.HubId
where u.UserName == User.Identity.Name select h.connection_str).First();
Cloud2Device c2d = new Cloud2Device(conn_str);
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Rp rp = db.Rps.SingleOrDefault(r => r.RpId == id);
if (rp == null)
{
return HttpNotFound();
}
//IoT stuff
try
{
c2d.EnableRaspberry("myDeviceId").Wait();
}
catch(Exception ex)
{
//do something
}
rp.is_enabled = enable;
db.SaveChanges();
return RedirectToAction("Index");
}
IoT utils:
public class Cloud2Device
{
private ServiceClient s_serviceClient;
public Cloud2Device(string conn_str)
{
s_serviceClient = ServiceClient.CreateFromConnectionString(conn_str);
}
public async Task EnableRaspberry(string deviceId)
{
var methodInvocation = new CloudToDeviceMethod("EnableRaspberry") { ResponseTimeout = TimeSpan.FromSeconds(2) };
var response = await s_serviceClient.InvokeDeviceMethodAsync(deviceId, methodInvocation);
Debug.WriteLine(response.GetPayloadAsJson());
}
}
The problem is that from the debug output I can see that exception Microsoft.Azure.Devices.Common.Exceptions.DeviceNotFoundException
was thrown however it is not handled by try-catch block.
console output
Application Insights Telemetry (unconfigured): "name":"Microsoft.ApplicationInsights.Dev.RemoteDependency","time":"2018-05-29T12:04:59.4748528Z","tags":{"ai.internal.sdkVersion":"rddf:2.2.0-738","ai.internal.nodeName":"5CG6455YJ4.ericsson.se","ai.cloud.roleInstance":"5CG6455YJ4.ericsson.se"},"data":{"baseType":"RemoteDependencyData","baseData":{"ver":2,"name":"/twins/myDeviceId/methods","id":"8/ZMgqG4iNc=","data":"https://utiliothub.azure-devices.net/twins/myDeviceId/methods?api-version=2017-10-15","duration":"00:00:01.2380000","resultCode":"404","success":false,"type":"Http","target":"utiliothub.azure-devices.net","properties":{"DeveloperMode":"true"}}}}
Exception thrown:'Microsoft.Azure.Devices.Common.Exceptions.DeviceNotFoundException' in Microsoft.Azure.Devices.dll
Exception thrown:'Microsoft.Azure.Devices.Common.Exceptions.DeviceNotFoundException' in mscorlib.dll
The thread 0x3834 has exited with code 0 (0x0).
Can anyone advise how I can catch and handle this exception in my application ? Thanks in advance for answer.
I'm not sure that error output is actually an exception being thrown up to your code. It could be ApplicationInsights just logging. However, I think the code is frozen due to calling Wait and blocking the return from the async method.
Make the controller method return type Task<ActionResult>, make that method async, then use await c2d.EnableRaspberry("myDeviceId"); to call that method.
See if doing that results in an exception (or success).
Related
I'm trying to figure out what to do with some errors in Unity when I call an API and how to propogate it up to the user interface - where and how to handle things. I've built aspnet APIs but there I'd normally use some error handling middleware to keep my controllers clean.
Let say we have some code like this (I'm using controller / repository language cos that's what I know).
A UI button fires an event like OnLoginButtonPressed.
An AuthController class reacts to the event by calling it's login method and then doing some logic when the response comes through, as follows:
public async void Login(LoginModel input)
{
var result = await AuthRepo.instance.Login(input);
app.token = result;
EventService.OnSuccessfulLogin();
}
The Auth.Repo calls the API and tries to return a Token class (just a wrapper around a JWT string)
public async Task<Token> Login(LoginModel input)
{
string json = JsonConvert.SerializeObject(input);
var request = UnityWebRequest.Post(app.baseURL + "authentication/login", json);
request.SetRequestHeader("Content-Type", "application/json");
request.SendWebRequest();
while (!request.isDone)
{
await Task.Yield();
}
if (request.result == UnityWebRequest.Result.Success)
{
Token token = JsonConvert.DeserializeObject<Token>(request.downloadHandler.text);
return token;
}
else
{
throw
}
}
So that's without exception handling. So I want to try to let the user know if there is a connection error, or they have put invalid details etc... I'm guessing I'm supposed add some logic into the AuthRepo such as:
if (request.result == UnityWebRequest.Result.Success)
{
Token token = JsonConvert.DeserializeObject<Token>(request.downloadHandler.text);
return token;
}
else if (request.result== UnityWebRequest.Result.ConnectionError)
{
throw new ConnectionException(request.error);
}
else if (request.result == UnityWebRequest.Result.DataProcessingError)
{
throw new BadRequestException(request.error);
}
else
{
throw new System.Exception(request.error);
}
This seems like a lot of code, and would end up in every method in every repo (unless I pull it out into some helper method?).... anyway, and then in the controller I would do something like:
try {
var result = await AuthRepo.instance.Login(input);
app.token = result;
EventService.OnSuccessfulLogin();
}
catch (ConnectionException ex)
{
EventService.OnConnectionError(ex.Message);
//some UI object would listen for this event and show the connection error message.
}
catch (BadRequestException ex)
{
EventService.LoginFailedError(ex.Message);
}
finally
{
EventService.UnknownError(ex.Message);
}
Is this completely down the wrong path? Seems like the code is just gonna get swamped with exception handling, or is this the correct way?
I've worked through a few YouTube videos that seem to suggest this is right, but they don't really show my use case (talking to APIs) so I'm just trying to be sure.
because UnityWebRequest.Result is an enum, you can start by using a switch statement here. Not only is this cleaner, it performs better too.
Another thing you can do is create an abstract class (e.g. APIException) and make that responsible for creating the correct exception instances, by giving it some static method like APIException FromUWRResult(UnityWebRequest.Result result).
Handling the exceptions can be done in APIException too. Give it an abstract method Handle() and implement accordingly in each of the deriving classes.
Now your code would look like this:
var ex = APIException.FromUWRResult(request.result);
if(ex != null) {
throw ex;
}
...
catch(APIException ex) {
ex.Handle();
}
I have the following test method. What I want to achieve in this test is to verify that the Error method was invoked by method SendSMSAsync() when it received an exception.
[TestMethod]
public async Task SendSMSAsync_PostAsyncRequestResultedInAnException_LogExceptionAsAnError
{
_client.Setup(clnt => clnt.PostAsync("uri", It.IsAny<HttpContent>()))
.ThrowsAsync(new Exception("Exception!"));
var service = new SMSService();
_ = service.SendSMSAsync("mobile_number");
_logger.Verify(lgr => lgr.Error(exception => exception.Message.Contains("Exception!")))
}
This is the implementation of the service.
public async Task<bool> SendSMSAsync(string mobileNumber)
{
try
{
...
// Set up PostAsync to throw an exception.
using (var response = _client.PostAsync("uri", content))
{
...
}
}
catch(Exception exception)
{
_logger.Error(exception);
throw new Exception("An error has occurred while sending an SMS.");
}
}
If I run this, my test fails saying that the Test method threw an exception.. Is there a way for me to verify if a method has been invoked inside the catch statement?
You could catch the exception as part of the test:
try
{
_ = service.SendSMSAsync("mobile_number");
}
catch
{
_logger.Verify(lgr => lgr.Error(exception => exception.Message.Contains("Exception!")));
}
To protect against false positives you might also return from the catch and intentionally fail the test after the catch. Something like:
try
{
_ = service.SendSMSAsync("mobile_number");
}
catch
{
_logger.Verify(lgr => lgr.Error(exception => exception.Message.Contains("Exception!")));
return;
}
throw new Exception("Test failed!");
The idea here is so the test doesn't "pass" if the method doesn't throw an exception in the first place.
Note that there may be tooling within the test framework to more gracefully fail a test, I don't recall off-hand. But the idea is generally the same.
Side note: Shouldn't there be an await on that method call?
So I have a database that fills up when a value is not found inside of it already, basically is a web API written in C# that checks if there is a value inside of it, if its null then calls another web service that will retrieve the data and insert a new record into the database and return the newly created record to the user. The code is like this:
[HttpGet ("{idDocument}")]
public async Task<IActionResult> GetPerson (string idDocument)
{
// Get the person record from the database
var person = await repository.GetPersonAsync (idDocument);
// if the record does not exist, return not found
if (person == null)
{
Person newPerson = await GetNewPersonFromRemoteServer(idDocument);
var result = mapper.Map<Person, PersonResource>(newPerson);
return Ok(result);
}
// Map the record to a resource to return to user
var resource = mapper.Map<Person, PersonResource> (person);
// return record resource to user
return Ok (resource);
}
private async Task<Person> GetNewPersonFromRemoteServer(string id)
{
Person newPerson = new Person();
string address = "http://remoteservice.serv/?id=";
string fullAddress = address + id;
try
{
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(new Uri(fullAddress));
string responseMessage = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
Person newPerson = JsonConvert.Deserialize<Person>(responseMessage);
repository.AddPersonAsync(newPerson);
await uow.CompleteAsync();
return newPerson;
}
return newPerson;
}
}
catch (Exception ex)
{
return newPerson;
}
}
But the service is working sometimes and sometimes its returning null even tho if I execute the remote service I get a response with the correct data. I believe it has something to do with the nested async/await methods/functions but I cant seem to figure it out a better solution to my problem. Any help would be greatly appreciated!
You have extremely faulty exception handling. Right now you swallow Exceptions. Moreover, you do it with Fatal Exceptions. That is a deadly sin of exception handling and can easily explain any odd behavior.
After a fatal exception your programm should always close. Ignoring them only leads to more followup errors that are impossible to debug or even predict.
You should read up on proper exception handling. Here are two articles I linked often:
http://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx
http://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET
I've been using azure table storage for years, and I'm not sure what the "proper" way to do this is with the newest WindowsAzure.Storage library, version 5.0.1-preview (for use in a new ASP.NET 5 application):
Problem:
Given a partition key and row key, delete the row without checking for existence first, and without failing if it does not exist.
Current Solution: This code works... but the exception handling is confusing:
public async Task DeleteRowAsync(CloudTable table, string partition, string row)
{
var entity = new DynamicTableEntity(partition, row);
entity.ETag = "*";
var op = TableOperation.Delete(entity);
try
{
await table.ExecuteAsync(op);
}
catch (Exception ex)
{
var result = RequestResult.TranslateFromExceptionMessage(ex.Message);
if (result == null || result.HttpStatusCode != 404)
throw ex;
}
}
Questions:
The exception itself pointed me to this TranslateFromExceptionMessage method... I can't find a whole lot of information on that and WrappedStorageException (the type of the exception that is thrown). Is this some kind of new/preferred way to check for 404 errors on storage exceptions? Does anyone know if all storage exceptions will now use this, or do I need to write code to test and figure it out?
There is an InnerException of type StorageException. Presumably our older code that used StorageException.RequestInformation.HttpStatusCode could access this inner exception in the same way. Is that "OK", or is parsing these new XML error messages better or more robust somehow?
Is there a different approach altogether that I should be considering for this case?
If you are using the latest client (Azure.Data.Tables), the delete method automatically swallows 404 responses and does not throw. This approach avoids the need to write code that introduces race conditions (checking first before performing an operations) or having to handle this condition with a try/catch block.
If you want to know if the operation actually deleted a table or it didn't exist, you can inspect the Status property of the response.
Response response = await tableClient.DeleteAsync();
if (response.Status == (int)HttpStatusCode.NotFound)
{
// entity didn't exist)
}
The RequestResult.TranslateFromExceptionMessage method is now marked [Obsolete] and I wanted a way to ignore 404's myself.
Based on your tip to check out the RequestInformation.HttpStatusCode I came up with the following:
try
{
await table.ExecuteAsync(op);
}
catch (StorageException storEx)
{
if (storEx.RequestInformation.HttpStatusCode != 404)
{
throw;
}
}
There is a similar approach found in the AspNet WebHooks project when configured to use Azure Table Storage. Take a look at the Microsoft.Aspnet.WebHooks.custom.AzureStorage StorageManager class.
I'm not sure this adds much on top of what you'd already found, but they handle everything without throwing an exception and always return a status code so you can react to that as necessary.
One difference here is they pass in the table and the operation to a multi-purpose ExecuteAsync method, rather than having one specifically for delete, but that's just an implementation detail.
Relevant code from their example:
public async Task<TableResult> ExecuteAsync(CloudTable table, TableOperation operation)
{
if (table == null)
{
throw new ArgumentNullException(nameof(table));
}
if (operation == null)
{
throw new ArgumentNullException(nameof(operation));
}
try
{
var result = await table.ExecuteAsync(operation);
return result;
}
catch (Exception ex)
{
var errorMessage = GetStorageErrorMessage(ex);
var statusCode = GetStorageStatusCode(ex);
var message = string.Format(CultureInfo.CurrentCulture, AzureStorageResources.StorageManager_OperationFailed, statusCode, errorMessage);
_logger.Error(message, ex);
return new TableResult { HttpStatusCode = statusCode };
}
}
public string GetStorageErrorMessage(Exception ex)
{
if (ex is StorageException storageException && storageException.RequestInformation != null)
{
var status = storageException.RequestInformation.HttpStatusMessage != null ?
storageException.RequestInformation.HttpStatusMessage + " " :
string.Empty;
var errorCode = storageException.RequestInformation.ExtendedErrorInformation != null ?
"(" + storageException.RequestInformation.ExtendedErrorInformation.ErrorMessage + ")" :
string.Empty;
return status + errorCode;
}
else if (ex != null)
{
return ex.Message;
}
return string.Empty;
}
public int GetStorageStatusCode(Exception ex)
{
return ex is StorageException se && se.RequestInformation != null ? se.RequestInformation.HttpStatusCode : 500;
}
I'm currently developing an app for windows phone 8.1 with Azure. (I wouldn't really recommend this to you after my experiences). Anyway... I've got Multiple Controller classes in it who really do the same (Check if something in my database already exists and if not it creates it). The problem I have has to be in the Read() function which checks the database if an entry already exists:
public async void Read(Device device)
{
IMobileServiceTable mobileServiceTable = Connect();
MobileServiceCollection<Device, Device> devices = null;
try
{
devices = await mobileServiceTable.MobileServiceClient.GetTable<Device>().Where(d => d.Manufacturer == device.Manufacturer && d.Model == device.Model).ToCollectionAsync();
}
catch (Exception e)
{
if (_onDeviceControllerListener != null)
{
_onDeviceControllerListener.OnError(ControllerError.Error.ReadFromDatabase, e.ToString());
}
return;
}
if (_onDeviceControllerListener != null && devices != null)
{
_onDeviceControllerListener.OnRead(devices);
}
}
This one works perfectly how it should but the oder one which is basically just a copy throws a NullReferenceException by the line "apps = await mobileServiceTab...":
public async void Read(Model.App app)
{
IMobileServiceTable mobileServiceTable = Connect();
MobileServiceCollection<Model.App, Model.App> apps = null;
try {
apps = await mobileServiceTable.MobileServiceClient.GetTable<Model.App>().Where(a => a.HardwareId == app.HardwareId && a.PackageId == app.PackageId).ToCollectionAsync();
}
catch (Exception e)
{
if (_onAppControllerListener != null)
{
_onAppControllerListener.OnError(ControllerError.Error.ReadFromDatabase, e.ToString());
}
return;
}
if (_onAppControllerListener != null)
{
_onAppControllerListener.OnRead(apps);
}
}
Does somebody know what the problem is?
Thanks for helping
You can add a try...catch block and check for the "MobileServiceInvalidOperationException" exception. Also take a look at the callstack to find out what's happening.
A fiddler trace would also let you know if you made a successful call to the Mobile Service to retrieve the data. Then you can determine if the issue lies in how you handle the data in your code.
Take another look at your MobileServiceContext class and ensure all properties are accounted for especially the properties that use the "Model.App" class. E.g
public virtual DbSet<Model.App> SomeProperty { get; set; }
Hope this helps.