Search code examples
c#angularasp.net-coreangular-http-interceptorsasp.net-core-middleware

Angular: Cannot return custom error message via HttpResponse


I've been googling the workaround and seen examples when we return error text in controller action as ActionResult/JsonResult or using HttpRequest approach as follows

HttpContext.Current.Response.Status = "error text";

For my back-end app I use ASP.NET Core 2.1.1 and .Status property is missing in HttpResponse class.

Moreover I can't find any properties which could contain my custom error message.

I use middleware class which grabs exception description and passes it as JSON

Startup.cs

app.UseMiddleware<ExceptionHandleMiddleware>();

The class itself

public class ExceptionHandleMiddleware
{
    private readonly RequestDelegate next;

    public ExceptionHandleMiddleware(RequestDelegate next)
    {
        this.next = next ?? throw new ArgumentNullException(nameof(next));
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await next(context);
        }
        catch (Exception ex)
        {
            context.Response.Clear();
            context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;

            await context.Response.WriteAsync(JsonConvert.SerializeObject(new { error = $"{ex.GetType().FullName}: '{ex.Message}'" }));
        }
    }
}

Have a look at the line

context.Response.StatusCode = StatusCodes.Status500InternalServerError;

This is required because in my Angular 6 app I use HttpInterceptor, and in order to catch error you're supposed to return an HTTP error (otherwise the .catch(...) block isn't being triggered in Angular interceptor).

Here's the bit from my Angular app

@Injectable()
export class ErrorHandlerInterceptor implements HttpInterceptor {

    constructor(
        private notify: NgNotify,
    ) { }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next
            .handle(req)
            .catch(this.handleError)
    }
    ...

Although context.Response.WriteAsync(...) bit does return the exception text, I am unable to extract it in .catch(...) block of the interceptor.

public handleError = (error: Response) => {

Here's error printed as JSON (note that it's lacking the error message saying "Last Entry")

{  
    "headers":{  
       "normalizedNames":{  

       },
       "lazyUpdate":null
    },
    "status":500,
    "statusText":"OK",
    "url":"https://localhost:44305/api/Entry/GetNext?id=11962",
    "ok":false,
    "name":"HttpErrorResponse",
    "message":"Http failure response for https://localhost:44305/api/Entry/GetNext?id=11962: 500 OK",
    "error":{  

    }
 }

Nevertheless, if I open chrome's Network tab I can see the following

enter image description here

So, I can't seem to fetch this error text from error: Response.

Perhaps, someone knows a better way of passing error to Angular client and fetching it there?

Update 1 - error handler (I've only put the breakpoint there in order to investigate the error's content)

public handleError = (error: Response) => {
    debugger
    return Observable.throw(error)
}

Solution

  • My oversight.

    If you look at the JSON returned

      ....
    
      "message":"Http failure response for https://localhost:44305/api/Entry/GetNext?id=11962: 500 OK",
      "error":{  
    
      }
    }
    

    error seems to be an empty object

    IN FACT error is a Blob which we are supposed to read in the following way

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next
            .handle(req)
            .catch(this.handleError)
    }
    
    public handleError = (error: Response) => {
    
        let reader = new FileReader();
    
        let ngNotify = this._ngNotify;
    
        reader.onload = function () {
    
            var result = JSON.parse(this.result);
    
            ngNotify.nofity('Error', result.error);
        };
    
        reader.readAsText(error['error']);
    
        return Observable.throw(error)
    }
    

    And that's it.