WebAPI how do I deserialize exceptions on the (.NET) client side - c#

I have a webAPI 2 controller, Currently it is throwing an exception, and this is being sent back to the client in a response message. 500 internal server error, the exception it's self is serialised and I can see the json representation of it in fiddler. (json pasted below)
I have been trying to find a way to de-serialize this exception back to a an .NET Exception object, but keep getting the error:
"Member 'ClassName' was not found."
currently in my client I am trying to deserialise the exception via the following code.
if (apiResponse.ResponseCode.Equals(500)) // Unhandled exception on server
{
var exceptionObject = await response.Content.ReadAsAsync<Exception>();
}
How do I properly de-serialize this exception object on my client side application? ideally I should be able to get back the original System.InvalidOperationException` object.
Below is the Response I am trying to de-serialize as captured in fiddler:
HTTP/1.1 500 Internal Server Error
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Wed, 30 Sep 2015 13:43:42 GMT
Content-Length: 1556
{"Message":"An error has occurred.","ExceptionMessage":"UrlHelper.Link must not return null.","ExceptionType":"System.InvalidOperationException","StackTrace":" at System.Web.Http.Results.CreatedAtRouteNegotiatedContentResult1.Execute()\r\n at System.Web.Http.Results.CreatedAtRouteNegotiatedContentResult1.ExecuteAsync(CancellationToken cancellationToken)\r\n at System.Web.Http.Controllers.ApiControllerActionInvoker.d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Filters.AuthorizationFilterAttribute.d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()"}

Thanks to the comment from Serg Rogovtsev, I now know that what you have is not an Exception but a HttpError. Therefore, you'll have to handle it as such:
var exceptionObject = await response.Content.ReadAsAsync<System.Web.Http.HttpError>();
I'm still not convinced that's a good idea, though. A 500 status code generally means that something went unexpectedly wrong and there was nothing there to catch it on the server side. If you have control over the server, I'd strongly suggest to handle such errors there.
The client is unable to do anything about it anyway, so all it needs is some kind of error notification.
Old answer (before the comment about HttpError):
A serialized Exception looks something like this:
{
"ClassName":"System.Exception",
"Message":"some exception",
"Data":null,
"InnerException":null,
"HelpURL":null,
"StackTraceString":null,
"RemoteStackTraceString":null,
"RemoteStackIndex":0,
"ExceptionMethod":null,
"HResult":-2146233088,
"Source":null,
"WatsonBuckets":null
}
What you have is just a formatted error message that contains information about the exception.
By the way: I wouldn't try to catch server-side exceptions on the client. You need to handle errors on the server and provide the client with enough information to inform the user about what happened.

I found a good way to get the json exception I want. I got this while looking through the samples on the ASPNET git repository.
In the ASPNET Startup.cs Configure method add the following (for dev environment only since as noted in other answers a 500 is probably not useful to the client)
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.Use(async (context, next) =>
{
try
{
await next();
}
catch (Exception ex)
{
if (context.Response.HasStarted)
{
throw;
}
context.Response.StatusCode = 500;
await context.Response.WriteJsonExcpetionAsync(ex);
}
});
}
// Other config logic...
}

Related

.Net Core Minio Client receives error on Ubuntu 16.04

Currently I'm using minio client .net core to store files to Digital Ocean Spaces. I have tested the current implementation on MacOS and the code uploads the file to s3. Once deployed to Ubuntu 16.04, I receiving the error below. Any help would be appreciated. Thanks
Stack Trace
Minio Exception: : Minio.Exceptions.MinioException: MinIO API responded with message=
at Minio.MinioClient.ParseError(IRestResponse response) in /q/.q/sources/minio-dotnet/Minio/MinioClient.cs:line 381
at Minio.MinioClient.HandleIfErrorResponse(IRestResponse response, IEnumerable`1 handlers, DateTime startTime) in /q/.q/sources/minio-dotnet/Minio/MinioClient.cs:line 526
at Minio.MinioClient.<ExecuteTaskAsync>d__84.MoveNext() in /q/.q/sources/minio-dotnet/Minio/MinioClient.cs:line 370
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Minio.MinioClient.<PutObjectAsync>d__20.MoveNext() in /q/.q/sources/minio-dotnet/Minio/ApiEndpoints/ObjectOperations.cs:line 489
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Minio.MinioClient.<PutObjectAsync>d__15.MoveNext() in /q/.q/sources/minio-dotnet/Minio/ApiEndpoints/ObjectOperations.cs:line 254
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Minio.MinioClient.<PutObjectAsync>d__14.MoveNext() in /q/.q/sources/minio-dotnet/Minio/ApiEndpoints/ObjectOperations.cs:line 200
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at CMTZ.Services.S3Service.<uploadS3File>d__8.MoveNext() in /home/rshaw/certified-mixtapes-core/CMTZ.Services/S3Service.cs:line 95
Trace
- - - - - - - - - - BEGIN REQUEST - - - - - - - - - -
PUT https://nyc3.digitaloceanspaces.com/cmtz/UploadedFiles/Mixtapes/test-upload-mixtape-20/cover.jpg HTTP/1.1
Host: nyc3.digitaloceanspaces.com:443
image/jpeg: System.Byte[]
x-amz-acl: public-read
Content-Type: image/jpeg
Content-MD5: ****************
x-amz-content-sha256: UNSIGNED-PAYLOAD
x-amz-date: 20191104T030850Z
Authorization: AWS4-HMAC-SHA256 Credential=*****************/20191104/us-east-1/s3/aws4_request, SignedHeaders=content-md5;host;x-amz-acl;x-amz-content-sha256;x-amz-date, Signature=fa6945a220238a7f877fb62d898d5596d9199860455499cec6bedccfb9cae0ed
- - - - - - - - - - END REQUEST - - - - - - - - - -
- - - - - - - - - - BEGIN RESPONSE - - - - - - - - - -
HTTP/1.1 403 Forbidden
x-amz-request-id: tx00000000000000ae68c6c-005dbf9642-334525e-nyc3a
Accept-Ranges: bytes
Date: Mon, 04 Nov 2019 03:08:50 GMT
Strict-Transport-Security: max-age=15552000; includeSubDomains; preload
Content-Length: 192
Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?><Error><Code>SignatureDoesNotMatch</Code><RequestId>tx00000000000000ae68c6c-005dbf9642-334525e-nyc3a</RequestId><HostId>334525e-nyc3a-nyc</HostId></Error>
- - - - - - - - - - END RESPONSE - - - - - - - - - -
Implementation
public async void uploadS3File(String filePath, String key, string type)
{
Console.WriteLine($"Uploading Object: {filePath}");
try
{
Dictionary<string, string> map = new Dictionary<string, string>();
map.Add("x-amz-acl", "public-read");
var minio = new MinioClient(Configuration.S3_HOST_ENDPOINT,
Configuration.S3_ACCESS_KEY, Configuration.S3_SECRET_KEY).WithSSL();
minio.SetTraceOn();
await minio.PutObjectAsync(Configuration.Bucket,
key, filePath, contentType: type, metaData: map);
}
catch (Exception ex)
{
Console.WriteLine($"Minio Exception: {ex}");
}
}
I was able to fix the issue above on Linux by upgrading my project target to .net core 2.1. There seem to be an issue with RestSharp. The library was removing the port number in asp.net core 2.0. Make sure to use the RestSharp version dependency in minio-client-dotnet. I upgraded to the latest version and received an error related to signature again when uploading objects with parenthesis and periods in the file name.

GetConversationMembersAsync returning 403 (Forbidden)

I'm working on a messaging extension and am trying to get the email address of the user who is using the app.
This app is being used internally and I'm using the email address to query Active Directory for the user's username.
When attempting to use the GetConversationMembersAsync method I receive a 403 (Forbidden) exception.
I am running this through IIS Express in Visual Studio 2017.
[BotAuthentication, TenantFilter]
public class MessagesController : ApiController
{
static string AppID = ConfigurationManager.AppSettings["MicrosoftAppId"];
static string AppPassword = ConfigurationManager.AppSettings["MicrosoftAppPassword"];
[HttpPost]
public async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
using (var connector = new ConnectorClient(new Uri(activity.ServiceUrl)))
{
var members = await connector.Conversations.GetConversationMembersAsync(activity.Conversation.Id);
System.UnauthorizedAccessException
HResult=0x80070005
Message=Authorization for Microsoft App ID XXX failed with status code Forbidden and reason phrase 'Forbidden'
Source=Microsoft.Bot.Connector
StackTrace:
at Microsoft.Bot.Connector.JwtTokenRefresher.d__2.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Net.Http.HttpClient.d__58.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
at Microsoft.Bot.Connector.Conversations.d__10.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Bot.Connector.ConversationsExtensions.d__11.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at NewApp.Controllers.MessagesController.d__2.MoveNext() in C:\Users\xxxx\source\repos\NewApp\NewApp\Controllers\MessagesController.cs:line 25
Inner Exception 1:
HttpRequestException: Response status code does not indicate success: 403 (Forbidden).
I found a similar issue here: Authorization for Microsoft App ID xxx failed with status code Forbidden and reason phrase 'Forbidden'. But that is not specifically for Microsoft Teams. connector-> Credentials->OAuthScope is showing "api.botframework.com/.default" but I believe that is correct for Teams.
My MicrosoftAppId and MicrosoftAppPassword are correct. If I comment out [BotAuthentication, TenantFilter] I get a 401 Unauthorized exception. If I comment out the line with GetConversationMembersAsync the app works.
I'm following the instructions here to attempt to get this context info: https://learn.microsoft.com/en-us/microsoftteams/platform/concepts/bots/bots-context.
I know this is an old question, but I had the same issue and found the solution, in case it helps someone. You need to be sure and add MicrosoftAppCredentials.TrustServiceUrl(serviceURL);, as follows:
using (var connector = new ConnectorClient(new Uri(activity.ServiceUrl)))
{
// the line below is the new required item
MicrosoftAppCredentials.TrustServiceUrl(serviceURL);
var members = await connector.Conversations.GetConversationMembersAsync(activity.Conversation.Id);
...

.Net Core ClientWebSocket: Unable to connect to server

I'm building a web application that connects to a Predix Timeseries instance to ingest data through a websocket. When I attempt to create the socket, I get this exception:
System.Net.WebSockets.WebSocketException: 'Unable to connect to the
remote server'
I'm using this code to create my websocket, with my error being thrown on the ConnectAsync call:
public async Task openWebSocket()
{
_socket = new ClientWebSocket();
_socket.Options.SetRequestHeader(headerName: "predix-zone-id", headerValue: PREDIX_ZONE_ID_HERE);
_socket.Options.SetRequestHeader(headerName: "authorization", headerValue: "Bearer " + AUTH_TOKEN_HERE);
_socket.Options.SetRequestHeader(headerName: "content-type", headerValue: "application/json");
CancellationToken token = new CancellationToken();
var uri = new Uri(uriString: "wss://gateway-predix-data-services.run.aws-usw02-pr.ice.predix.io/v1/stream/messages");
await _socket.ConnectAsync(uri: uri, cancellationToken: token);
}
and here is my exception's stack trace:
System.Net.WebSockets.WebSocketException occurred
HResult=0x80004005
Message=Unable to connect to the remote server
Source=<Cannot evaluate the exception source>
StackTrace:
at System.Net.WebSockets.WebSocketHandle.<ConnectAsyncCore>d__20.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Net.WebSockets.ClientWebSocket.<ConnectAsyncCore>d__16.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at PROJECTNAME.Services.TimeseriesService.<openWebSocket>d__6.MoveNext() in %PROJECTLOCATION%\Services\%SERVICENAME%.cs:line 34
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at PROJECTNAME.Services.TimeseriesService.<Initialize>d__5.MoveNext() in %PROJECTLOCATION%\Services\%SERVICENAME%.cs:line 21
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at PROJECTNAME.Program.<Initialize>d__1.MoveNext() in %PROJECTLOCATION%\Program.cs:line 52
Inner Exception 1:
WebSocketException: Unable to connect to the remote server
The inner exception stack track has little more information, but noting the ThrowOnInvalidConnectState() that might be useful:
at System.Net.WebSockets.WinHttpWebSocket.ThrowOnInvalidConnectState()
at System.Net.WebSockets.WinHttpWebSocket.<ConnectAsync>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Net.WebSockets.WebSocketHandle.<ConnectAsyncCore>d__20.MoveNext()
I'm not sure what else to try - lots of other posts on SO at least have some useful information in the inner exception.
I've also attempted to use Fiddler. I can see message for other services, including my auth token retrieval from UAA, but nothing from this service.
Am I missing some configuration steps somewhere? Any help or guidance would be appreciated.
I figured out the issue. The three required headers for the Predix Timeseries are:
Predix-Zone-Id: zone_id_here
Authorization: Bearer auth_token_here
Origin: "https:// hostname_here
My problem is that I tried the Origin header, but the Predix VCAP_APPLCIATION environmental variable for the application_uris doesn't include the "https://". Once I added it manually ("https://" + uri) it worked fine.
I haven't done this in C#, but "Unable to connect" sounds like it could be a network issue. Can you open a websocket to another server? Maybe this one?
https://www.websocket.org/echo.html
Could be a proxy server issue? Can you try from a different network?

Return a Status Code 304 in Middleware

I'm trying to return a 304 Not Modified status code in my ASP.Net Core application when a file is accessed via my middleware.
I try setting the HTTP Status Code and sending the response back like so:
context.Response.StatusCode = 304;
await context.Response.WriteAsync("Not Modified");
But I get this exception:
System.InvalidOperationException occurred HResult=-2146233079
Message=Write to non-body 304 response.
Source=Microsoft.AspNetCore.Server.Kestrel StackTrace:
at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame.HandleNonBodyResponseWrite()
at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame.d__183.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Adapt.REST.ServeStaticFilesMiddleware.d__8.MoveNext() in
C:\AdaptSource\Xivic\Adapt.Services\ServeStaticFilesMiddleware.cs:line
113 InnerException:
So obviously I can't use WriteAsync or SendFileAsync because they both send a response that contains a body.
How do I send a 304 back to the client without a body?
Simply doing nothing after setting the status code causes the intended functionality.
context.Response.StatusCode = 304;
return;

"Task was canceled" errors on Azure website

As traffic to our Azure website has increased, we've been seeing an increasing number of "A task was canceled" errors. A typical trace might look like this:
Fatal web api error: Controller: CustomerUserEventsController;
Url: http://app.payboard.com/api/organizations/9ddf55d1-e0c1-4a8f-9327-eef38682e090/addcustomeruserevent?callback=jQuery210035782216349616647_1398442710964&cookieId=05be2755-dc0d-414d-b0d2-ea1986a929c3&customerId=&customerName=&customerUserId=&customerUserFirstName=&customerUserLastName=&eventName=hr-index-GET&_=1398442710965;
Error: A task was canceled. ( at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__0.MoveNext())
We used to maybe get one a day; it's been increasing gradually, so that now we're getting a couple dozen.
You'll notice that there's only framework code in the stack trace - none of our code is in there at all. So I'm a bit at a loss for how to troubleshoot it. Any suggestions on how to approach tracking these down?
See the answer here:
ASP.NET Web API OperationCanceledException when browser cancels the request
And the WebAPI bug here:
http://aspnetwebstack.codeplex.com/workitem/1797
Basically, it's expected when an HTTP request is canceled, for instance, if the user closes the page that initiated the request. To keep it from showing up in your logs, basically, you just need to create a custom message handler that suppresses the error:
/// <summary>
/// See https://stackoverflow.com/questions/22157596/asp-net-web-api-operationcanceledexception-when-browser-cancels-the-request
/// </summary>
public class CancelledTaskBugWorkaroundMessageHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
// Try to suppress response content when the cancellation token has fired; ASP.NET will log to the Application event log if there's content in this case.
if (cancellationToken.IsCancellationRequested)
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
return response;
}
}
Had the exact same problem. In my case the solution was to change the authority (an https URL) from https://login.windows.net/... to https://login.microsoftonline.com/... as it could not be reached anymore. I am not publishing the full authority as it is a specific one for our organisation. Theoretically the first one is more direct, so I used that based on advise from a Microsoft specialist. But if it cannot be reached then there is a problem. Will need to go figure out nw why it is unreachable.
Hope this will be a lead to resolve your issues.
Regards,
Coen.

Categories