Home > OS >  How to catch bad URL token eror when calling .NET 5 ASP.NET MVC REST API C# is called? Middleware do
How to catch bad URL token eror when calling .NET 5 ASP.NET MVC REST API C# is called? Middleware do

Time:10-05

How to catch this error in the back-end C# code?

See the solution usedat the end of the question description.

It is due to integer overflow for a HTTP GET URL where the URL contains an integer parameter. Calls with a 32 bit integer sized value work (e.g., "1234" works and the API is called).

HTTP GET www.domain.net/api/product/{productIdInteger}

Where the JavaScript caller calls HTTPS

www.domain.net/api/product/222222222222222222222222222222222222

The response looks like:

{
  "errors": {
    "productIdInteger": [
      "The value '222222222222222222222222222222222222' is not valid."
    ]
  },
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-f6a8a724853f3f44ba9fc6468a42ad94-aca067537e73b645-00"
}

Startup.cs adds a middleware to catch exceptions at the WebAPI controller level.

This does not catch the URL parameter parsing error or a bad JSON in HTTP Body parsing error

Startup.cs has app.UseMiddleware();

MyErrorMiddleware has this invoke method and MyExceptionHandlerAsync is never called for this case.

public async Task Invoke(HttpContext context)
{

    try
  {
    //This line gets called
    await _next(context);
  }
  catch (Exception ex)
  {
   //The catch block is not executed if the integer passed to the controller method overflows a 32 bit integer
//The catch block is executed if an C# WebAPI controller method is called and the controller method throws an exception
    await MyExceptionHandlerAsync(context, ex);
  }
}

Here is what worked best for our application.

  1. Create base class for controller methods (code below) having a Boolean errorFlag and a List ErrorList
  2. Derive the return objects for controller methods off the base class in #1. Each of the return classes had the error properties plus the data returned from the controller method. The data property returned for the controller would be called "ReturnData" to make the front-end JavaScript easier.
  3. Add the model validation error handler to Startup.cs. Return the same base class as in step #1
  4. Add in the error handling middleware catch block, code to return the error using the same error class from step #1

This handles 1) business error from controller (not a thrown exception), 2) exception thrown from controller or a method it calls, no need to have a try/catch in the controller, 3) error passing data to the WebAPI controller method (model validation error)

public class ReturnBaseClass
{
  public ReturnBaseClass() { errorList = new List<string>()}
  public bool errorFlag {get;set;}
  public List<string> errorList {get;set;}
}

//A controller return data could look like

public class CourseDataReturn : ReturnBaseClass
{
  //Always called ReturnData to make front-end JavaScript easier
  public CourseInfo ReturnData {get;set;}
}

//CourseInfo is the data returned from the data repository method called from the controller.
public class CourseInfo
{
  public int CourseId {get;set;}
  public string CourseName {get;set;}
}

We simplified our return HTTP status codes to OK, Bad Request, Internal Server Error for back-end WebAPI calls which are made. All other HTTP status codes would be a HTTP transport error (timeout, not found (URL is bad), ....

That got us past the REST mismatch of HTTP NotFound being either the URL is not found, such as domain name does not exist, and the database row being retrieved for a HTTP GET student by student ID not finding the student.

CodePudding user response:

It is not an exception. It is ModelState not valid. So you can catch this error by:

Step 1:

Add ConfigureApiBehaviorOptions in Program.cs file:

builder.Services.AddControllers().ConfigureApiBehaviorOptions(options => {
  options.SuppressModelStateInvalidFilter = true; 
});

Step 2:

Handle errors in top of Controller file:

if (!ModelState.IsValid) {
  var errors = new List<string>();
  foreach (var state in ModelState)
  {
    foreach (var error in state.Value.Errors)
    {
        errors.Add(error.ErrorMessage);
    }
  }
  // Do something to handle error list
}

CodePudding user response:

The code below is based on [https://briancaos.wordpress.com/2019/11/29/net-core-catch-model-binding-exceptions/]

We used a variation on this code in the solution

//In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
... //all of the configuration for services, etc...

//Add this at the very end of ConfigureServices()
//Handle errors - model bind error, bad parameter passed to WebAPI endpoint, ...
services.Configure<ApiBehaviorOptions>(options => 
  options.InvalidmsResponseFactory = (ac =>
  {
  var ms = ac.ms;

  //Properties related to errors: ms.IsValid, s.ErrorCount, ms.ValidationState
  //var errors = ms.Where(x => x.Value.Errors.Count > 0);
  var errs = new ValidationProblemDetails(ac.ms);

  var errList = new List<string>();

  //errList.Add($"Detail: {errs.Detail}");
  errList.Add($"Title: {errs.Title}");
  //errList.Add($"URL: {errs.Instance}");
  //errList.Add($"HTTP Status Code: {errs.Status}");
  //errList.Add($"Error Type: {errs.Type}");

  List<string> mlist = new List<string>();
  foreach (string k in errs.Errors.Keys)
  {
    //build one message line <KEY>: <ERROR MESSAGE 1>, <ERROR MESSAGE 2>, ...
    string m = $"{k}: ";

    string[] value = errs.Errors[k];
    if (value.Length > 0)
    {
      m = $"{m}{string.Join(",", value)}";
    }

    mlist.Add(m);
  }

  string r = "";
  if (msgList.Any())
  {
    r = string.Join(",", msgList);
  }

  return new BadRequestObjectResult(r);
  }
  ));
  • Related