简体   繁体   中英

Displaying Custom Server Side Error Message in Angular and .Net Core API

I have some rest API written in C# and the API is called from Angular (I am using version Angular 8). The call is fine and it is working fine. However, in case of any exception, I cannot display the customized error message in angular. For example, suppose I have a server side validation in C# which validates if the value of a field matches with the string "abc". If it does not match, it will throw an error and in UI (developed in Angular), I want to display the message

"Invalid String Specified".

My server side code is as below -

if (headerValues.Equals("abc")) {
    throw new InvalidStringException("Invalid String specified", 999);
}

The Invalid InvalidStringException class is as below -

public class InvalidStringException : System.Exception
{
    int status { get; set; }
    public InvalidStringException() { }
    public InvalidStringException(string message, int status) : base(message) {
        this.status = status;
     }
}

When that exception is thrown and caught in server side, it is available as 500 exception but could not print the custom message.

I am trying following code in Angular -

} catch (error) {
  console.log("Error Status: ", error.status);
  console.log("Error Status: ", error.message);
}

Please suggest how to handle that scenario.

The error object that your Angular app receives should be an instance of HttpErrorResponse

You could do something like this to handle http errors:

if (error instanceof HttpErrorResponse) {
  if (!error.status) {
    console.log(error.message || error.toString());
  } else {
    console.log(`error status : ${error.status} ${error.statusText}`);
    switch (error.status) {
      case 401:
        this.router.navigateByUrl("/login");
        break;
      case 500:
        this.router.navigateByUrl("/login");
        console.log(`redirect to login`);
        break;
    }
  }
} else {
  console.error("Other Errors");
}

Are you explicitly catching the InvalidStringException in your .NET API controller and returning the custom message? If not, the response will be a generic HTTP 500 'Internal Server Error' response. I'd suggest explicitly catching the InvalidStringException in your .NET API controller and returning a 400 response with your custom message eg

try {
    ...
}
catch (InvalidStringException iex) {
    return BadRequest(iex.message); // iex.message == Invalid String specified
}

When the InvalidStringException scenario occurs, This will return a HTTP 400 response with "Invalid String specified" as the response body. You should be able to log the error on Angular side as you're currently doing...

You are throwing an exception which is handled by C# exception handler and it will only return the custom error message specified in that handler.

To return a custom message, you need to return with http code like 4xx or 5xx.

new HttpResponseException(Request.CreateErrorResponse(System.Net.HttpStatusCode.Conflict, "Custom Message"));

Or you can return with 2xx and you have to parse this subscribe or then method eg

new System.Web.Http.Results.ResponseMessageResult(
                    Request.CreateResponse((HttpStatusCode)227, "Custom Error Message")
                );

this.http.get().toPromise().then((response: any) => {
        if (response.status == 227) {
            return error;
        } else {
            return data;
        }
        return apiResponse;
    }).catch(error => {
        //nothing here
    });

If throwing a exception is not really necessary, you can return status code 400 and a message using BadRequest :

if (headerValues.Equals("abc")) {
    return BadRequest("Invalid String specified");
}

you can use http interceptor to create general error handler for all http error in angular app,this way you can use alert ,redirect to login page in case token expired ,overwrite the error object and more but you can still access to the error object at the component level by add a callback for observable error.

Error Handler Service

@Injectable()
export class ErrorHandlerService implements HttpInterceptor {
  constructor(private msgServ: MessageService) {}
  public intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      catchError((err: HttpErrorResponse) => {
        switch (err.status) {
          case 500: {
            this.msgServ.add({
              severity: "error",
              summary: "Error ",
              detail: "Server is gone..💀"
            });

            break;
          }
          case 400: {
            this.msgServ.add({
              severity: "error",
              summary: "Error ",
              detail: "custome error message..."
            });

            break;
          }
          case 401: {
            if (err.message == "invalid_token") {
              // router 👉 navigate to login
            }
            break;
          }

          default: {
            this.msgServ.add({
              severity: "error",
              summary: "Error ",
              detail: err.message
            });
          }
        }

        return throwError(err);
      })
    );
  }
}

add the Interceptor to Providers in app module

@NgModule({
  ....
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: ErrorHandlerService, multi: true },
    MessageService
  ],
  ....
})
export class AppModule {}

demo 🚀

MessageService is related to primeng component library ,you can use your own alert structure

As other people have mentioned, you need to catch the exception and convert it to an appropriate HTTP response in your own code.

The reason for that is because if otherwise your exception is handled by ASP.NET Core using exception handling configuration you have, and it may vary:

With developer exception page

Usually in development, you will have code:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

What it does is when your environment is Development , it turns on a special page for developers to see information of unhandled exceptions. It is only in this case, you get the exception stacktrace as well as the exception message in the response body.

Without developer exception page

Conversely, if the exception page is turned off (usually off for Production environment), you will see nothing in the response body.

How to fix

Given exception handling in ASP.NET Core is a cross-cutting concern, I wouldn't use try...catch around everywhere InvalidStringException needs to be converted to HttpResponse .

I would instead use either an IActionFilter or use UseExceptionHandler which is the exception handling middleware :

Here is an example of using UseExceptionHandler in Configure method in Startup.cs :

app.UseExceptionHandler(opt => opt.Run(
    async ctx =>
    {
        var feature = ctx.Features.Get<IExceptionHandlerFeature>();
        if (feature.Error is InvalidStringException ex)
        {
            await ctx.Response.WriteAsync(ex.Message);
        }
    }));

In this way, your InvalidStringException is handled globally in your application, without explicit try...catch . And you could throw the exception anywhere in your code, the above code would catch the exception and properly convert it to an HTTP response with your own message as the body.

Also note, because you are calling the API from an Angular app, so chances are you might need to set CORS up in your API application if the two applications run from different origins.

Without CORS, your HTTP request from the Angular app may fail before it can reach your API. In this case, the status of the HTTP response in your Angular app may be undefined . And in your console, you could see CORS errors.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM