Nancy Exception in RequestStartup - exception

I'm using Nancy to create a web api. I have a signed token that is passed in from the user to authenticate. This authentication is doen in the RequestStartup method in my own Bootstrapper. Now in some cases, for instance when I can't veryfy the signed token I would like to just be able to throw an exception and have that handled byt the OnError hanhdler in Nancy. However an exception thrown before the RequestStartup is finsihed isn't caught. The request generates a 500 error and I would like to return something else with my own error information.
I have the obvious case where I throw an exception but also possibilities of an exception being thrown in the GetIdentity() method.
I'm looking for any input in how to handle this.
protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
{
base.RequestStartup(container, pipelines, context);
pipelines.OnError.AddItemToStartOfPipeline((ctx, exception) =>
container.Resolve<IErrorHandler>().HandleException(ctx, exception));
var identity = container.Resolve<IAuthenticationController>().GetIdentity();
var configuration = new StatelessAuthenticationConfiguration(_ => identity);
StatelessAuthentication.Enable(pipelines, configuration);
var logManager = new LogManager(context);
pipelines.AfterRequest.AddItemToEndOfPipeline(_ => logManager.Log());
try
{
X509Certificate2 clientCert = context.Request.ClientCertificate as X509Certificate2;
container.Resolve<ICertificateValidator>().Validate(clientCert);
}
catch (Exception ex)
{
throw new MklServerAuthenticationException(ErrorCodes.WrongOrNonexistingCertificate, ex);
}
}

Figured out a way to solve the above problem and thought somebody else might like to know. Replace the line in my code above, containing the GetIdentity() call, with the following:
Identity identity = null;
try
{
identity = container.Resolve<IAuthenticationController>().GetIdentity(requestInfo);
}
catch (Exception ex)
{
var exception = new MklAuthentcationException(ErrorCodes.TokenInvalid, ex);
context.Response = container.Resolve<IErrorHandler>().HandleException(context, exception);
pipelines.BeforeRequest.Invoke(context, CancellationToken.None);
}
I'm using the fact stated in nancy that:
The PreRequest hook is called prior to processing a request. If a hook returns a non-null response then processing is aborted and the response provided is returned.
So by setting a response (my error in this case) on the PreRequest hook and invoking it my error is returned and execution is stopped.
Maybe not the nicest solution... If you can figure out something better please let me know.

Related

Nestjs throw exception from empty result in the controller

I have a control that calls a service.
If the service returns an empty payload from the db I want to throw an exception.
at the moment I am doing that in the service:
this is the service I have at the moment with the exception.
async getPreferences(eUserId: string): Promise<UserPreferences> {
const userPreferences = await this.userPreferencesModel.findOne({
eUserId,
});
if (!userPreferences) {
throw new NotFoundException("We couldn't find your user preferences");
}
return userPreferences;
}
I want the controller to handle the exception, The issue is that the controller response is a Promise.
How can I handle that?
This is what I shave done:
#Get()
async getPreferences(
#Headers('x-e-user-id') eUserId: string,
): Promise<UserPreferences> {
const userPreferences = this.userPreferencesService.getPreferences(eUserId);
console.log('userPreferences: ', userPreferences);
// Here is what I am trying to monitor...
if (userPreferences) {
throw new NotFoundException("We couldn't find your user preferences");
}
return userPreferences;
}
Ther console.log in the controller returns:
userPreferences: Promise { <pending> }
Now, if the service response is empty no exception is thrown.
How can I monitor the service result in order to throw an exception
Multiple ways you can solve this. Here's one.
Don't throw an error in your service, just return the result or null.
async getPreferences(eUserId: string): Promise<UserPreferences | null> {
return this.userPreferencesModel.findOne({
eUserId,
});
}
Then in your controller await for the result, you forgot this. That's why you are seeing a pending promise. After the result has been resolved, check if any user preferences were returned and throw the NotFoundException if not.
#Get()
async getPreferences(#Headers('x-e-user-id') eUserId: string): Promise<UserPreferences> {
const userPreferences = await this.userPreferencesService.getPreferences(eUserId);
if (!userPreferences) {
throw new NotFoundException("We couldn't find your user preferences");
}
return userPreferences;
}
I would not throw NotFoundException or any other HTTP related error from your service. Leave that responsibility up to your controller, don't tie your service (logic) to HTTP error codes. Throw errors here that are not aware of the context (HTTP) they are being used in.
PS: You might also consider passing the user ID via the query string or as a route parameter instead of via the headers.

Why HttpClient.GetFromJsonAsync doesn't throw an exception when the response is HTML instead of JSON?

I'm learning Blazor.
I have created a Blazor WASM App with the "ASP.NET Core Hosted" option.
So I have 3 projects in the solution: Client, Server and Shared.
The following code is in the Client project and works perfectly when the endpoint is correct (obviously). But at some point I made a mistake and messed up the request URI, and then I noticed that the API returned an HTML page with code 200 OK (as you can see in the Postman screenshot below the code).
I expected one of my try-catches to get this, but the debugger jumps to the last line (return null) without throwing an exception.
My first question is why?
My second question is how can I catch this?
I know fixing the endpoint fixes everything, but would be nice to have a catch that alerts me when I have mistyped an URI.
Thanks.
private readonly HttpClient _httpClient;
public async Task<List<Collaborator>> GetCollaborators()
{
string requestUri = "api/non-existent-endpoint";
try
{
var response = await _httpClient.GetFromJsonAsync<CollaboratorsResponse>(requestUri);
if (response == null)
{
// It never enters here. Jumps to the last line of code.
}
return response.Collaborators;
}
catch (HttpRequestException)
{
Console.WriteLine("An error occurred.");
}
catch (NotSupportedException)
{
Console.WriteLine("The content type is not supported.");
}
catch (JsonException)
{
Console.WriteLine("Invalid JSON.");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return null;
}
it is a never good idea to use GetFromJsonAsync, You are not the first who are asking about the strange behavior. Try to use GetAsync. at least you will now what is going on.
var response = await client.GetAsync(requestUri);
if (response.IsSuccessStatusCode)
{
var stringData = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<CollaboratorsResponse>(stringData);
... your code
}
else
{
var statusCode = response.StatusCode.ToString(); // HERE is your error status code, when you have an error
}

How to throw a future through waitFor in Dart

I'm the author of the Dart dshell package.
https://pub.dev/packages/dshell
Dshell is a library and tooling for writing dart cli scripts.
Dshell uses waitFor to hide futures from users as they serve little use in the typical cli application.
My problem is that if a future throws an unhandled exception whilst being handled by waitFor, it essentially shuts the application down.
I need to be able to capture any exception and then let the caller decided what to do with the exception.
Here is what I've tried so far. No of the catch techniques will capture the unhandled exception:
import 'dart:async';
import 'dart:cli';
void main() {
var future = throwException();
try {
future
.catchError((Object e, StackTrace st) => print('onErrr: $e'))
.whenComplete(() => print('future completed'));
print(waitFor<int>(future));
} // on AsyncError
catch (e) {
if (e.error is Exception) {
print(e.error);
} else if (e is AsyncError) {
print('Rethrowing a non DShellException ${e}');
rethrow;
} else {
print('Rethrowing a non DShellException ${e}');
rethrow;
}
} finally {
print('waitForEx finally');
}
}
Future<int> throwException() {
var complete = Completer<int>();
Future.delayed(Duration(seconds: 2), () => throw Exception());
return complete.future;
}
The dart waitFor has a line that makes me think this may not be possible:
If the Future completes normally, its result is returned. If the Future completes with an error, the error and stack trace are wrapped in an AsyncError and thrown. If a microtask or message handler run during this call results in an unhandled exception, that exception will be propagated as though the microtask or message handler was the only Dart invocation on the stack. That is, unhandled exceptions in a microtask or message handler will skip over stacks suspended in a call to waitFor.
So I'm a little confused by the difference between a 'Future completes with an error' and 'a microtask ... results in an unhandled exception'.
The Future returned by your throwException will never complete with either a value or an error. The error thrown by the Future.delayed is an unhandled async error, it is unrelated entirely to the Future that is returned from that method. The ways to get a Future that completes with an error are:
The Future.error constructor.
Using Completer.completeError on a not yet completed Completer.
Using throw in an async method.
Using throw in a callback passed to a Future constructor, or .then.
So in your example, the Future.delayed creates a Future that will complete with an error because of the throw in the callback. Nothing is listening on this Future. There is no await, no .then or .catchError chained off of it. Once a Future completes with an error, and it has no handlers for that error, it will bubble up to the surrounding error zone. See https://dart.dev/articles/archive/zones#handling-asynchronous-errors
If you want to be able to react to unhandled errors you can use runZoned - getting the details right can be tricky. Note that it's possible to have multiple unhandled async errors resulting from running some bit of code, and that the completion of a Future does not necessarily mean that there aren't other unhandled async errors that can surface later.
From Nate Bosch I've devised a possible answer:
I hadn't realised that you can add multiple onCatchError methods to a future.
In DShell I'm passed the future so I had assumed I couldn't modify it.
So I added an onCatchError to the Future.delayed and then use the completer to pass the error back up the correct stack.
So this seems to work, I'm just uncertain if I need to actually implement a zone to cast my catch net a little further?
import 'dart:async';
import 'dart:cli';
void main() {
var future = throwExceptionV3();
try {
future
.catchError((Object e, StackTrace st) => print('onErrr: $e'))
.whenComplete(() => print('future completed'));
print(waitFor<int>(future));
} // on AsyncError
catch (e) {
if (e.error is Exception) {
print(e.error);
} else if (e is AsyncError) {
print('Rethrowing a non DShellException ${e}');
rethrow;
} else {
print('Rethrowing a non DShellException ${e}');
rethrow;
}
} finally {
print('waitForEx finally');
}
}
Future<int> throwExceptionV3() {
var complete = Completer<int>();
try
{
var future = Future.delayed(Duration(seconds: 2), () => throw Exception());
future.catchError((Object e) {
print('caught 1');
complete.completeError('caught ') ;
});
}
catch (e)
{
print ('e');
}
return complete.future;
}

Is there any way within middleware running on ASP.NET Core 2.2 to detect if the request is for an ApiController?

I have an application with both MVC and 'new' ApiController endpoints in ASP.NET Core 2.2 co-existing together.
Prior to adding the API endpoints, I have been using a global exception handler registered as middleware using app.UseExceptionHandler((x) => { ... } which would redirect to an error page.
Of course, that does not work for an API response and I would like to return an ObjectResult (negotiated) 500 result with a ProblemDetails formatted result.
The problem is, I'm not sure how to reliably determine in my 'UseExceptionHandler' lambda if I am dealing with an MVC or a API request. I could use some kind of request URL matching (eg. /api/... prefix) but I would like a more robust solution that won't come back to bite me in the future.
Rough psuedo-code version of what I'm trying to implement is:
app.UseExceptionHandler(x =>
{
x.Run(async context =>
{
// extract the exception that was thrown
var ex = context.Features.Get<IExceptionHandlerFeature>()?.Error;
try
{
// generically handle the exception regardless of what our response needs to look like by logging it
// NOTE: ExceptionHandlerMiddleware itself will log the exception
// TODO: need to find a way to see if we have run with negotiation turned on (in which case we are API not MVC!! see below extensions for clues?)
// TODO: ... could just use "/api/" prefix but that seems rubbish
if (true)
{
// return a 500 with object (in RFC 7807 form) negotiated to the right content type (eg. json)
}
else
{
// otherwise, we handle the response as a 500 error page redirect
}
}
catch (Exception exofex)
{
// NOTE: absolutely terrible if we get into here
log.Fatal($"Unhandled exception in global error handler!", exofex);
log.Fatal($"Handling exception: ", ex);
}
});
});
}
Any ideas?
Cheers!
This might be a bit different than what you expect, but you could just check if the request is an AJAX request.
You can use this extension:
public static class HttpRequestExtensions
{
public static bool IsAjaxRequest(this HttpRequest request)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
if (request.Headers == null)
return false;
return request.Headers["X-Requested-With"] == "XMLHttpRequest";
}
}
And then middleware with an invoke method that looks like:
public async Task Invoke(HttpContext context)
{
if (context.Request.IsAjaxRequest())
{
try
{
await _next(context);
}
catch (Exception ex)
{
//Handle the exception
await HandleExceptionAsync(context, ex);
}
}
else
{
await _next(context);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
//you can do more complex logic here, but a basic example would be:
var result = JsonConvert.SerializeObject(new { error = "An unexpected error occurred." });
context.Response.ContentType = "application/json";
context.Response.StatusCode = 500;
return context.Response.WriteAsync(result);
}
see this SO answer for a more detailed version.
If you want to check whether the request is routed to ApiController, you could try IExceptionFilter to hanlde the exceptions.
public class CustomExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
if (IsApi(context))
{
HttpStatusCode status = HttpStatusCode.InternalServerError;
var message = context.Result;
//You can enable logging error
context.ExceptionHandled = true;
HttpResponse response = context.HttpContext.Response;
response.StatusCode = (int)status;
response.ContentType = "application/json";
context.Result = new ObjectResult(new { ErrorMsg = message });
}
else
{
}
}
private bool IsApi(ExceptionContext context)
{
var controllerActionDesc = context.ActionDescriptor as ControllerActionDescriptor;
var attribute = controllerActionDesc
.ControllerTypeInfo
.CustomAttributes
.FirstOrDefault(c => c.AttributeType == typeof(ApiControllerAttribute));
return attribute == null ? false : true;
}
}
Thanks to all of the advice from others, but I have realised after some more thought and ideas from here that my approach wasn't right in the first place - and that I should be handling most exceptions locally in the controller and responding from there.
I have basically kept my error handling middleware the same as if it was handling MVC unhandled exceptions. The client will get a 500 with a HTML response, but at that point there isn't much the client can do anyway so no harm.
Thanks for your help!

how to throw and catch web api exception?

I am trowing Exception from WebApi as shown below
Catch(ex)
{
var rEx = HttpResponseException(r.CreateErrorResponse(HttpStatusCode.NotFound,ex));
throw rEx;
}
My question is "How to catch this Exception in Windows Application";
i.e. to catch this exception from where I am calling Web Api Method????
You will receive response with http code 500 - internal server code. So, to "catch" this exception just check response code in calling app.
That would throw a 404 response back to the client. Assuming you're using WebClient to call this service, it can be done in a couple of ways:
1) You can check for the status of the response like this:
HttpResponseMessage response = await client.GetAsync("api/products/1");
if (response.IsSuccessStatusCode)
{
//write app code.
}
2) Or.. in a try/catch fashion by checking: resp.EnsureSuccessStatusCode(), which would throw an exception if the response is not 200. Like this:
try
{
HttpResponseMessage response = await client.GetAsync("api/products/1");
resp.EnsureSuccessStatusCode(); // Throw if not a success code.
// ...
}
catch (HttpRequestException e)
{
// Handle exception.
}
It is discussed in detail here