Отображение пользовательского сообщения об ошибке на стороне сервера в Angular и. Net Core API - PullRequest
6 голосов
/ 20 марта 2020

У меня есть некоторый API покоя, написанный на C#, и API вызывается с Angular (я использую версию Angular 8). Звонок в порядке, и он работает нормально. Однако в случае каких-либо исключений я не могу отобразить настроенное сообщение об ошибке в angular. Например, предположим, у меня есть проверка на стороне сервера в C#, которая проверяет, совпадает ли значение поля со строкой "ab c". Если он не совпадает, он выдаст ошибку, и в пользовательском интерфейсе (разработан в Angular) я хочу отобразить сообщение

«Указана неверная строка».

Мой код на стороне сервера, как показано ниже -

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

Недопустимый класс InvalidStringException, как показано ниже -

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

Когда это исключение выдается и перехватывается на стороне сервера, оно доступно как исключение 500, но не может напечатать пользовательское сообщение.

Я пытаюсь использовать следующий код в Angular -

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

Пожалуйста, предложите, как обработать этот сценарий.

Ответы [ 7 ]

5 голосов
/ 28 марта 2020

Вам нужен новый экземпляр класса HttpErrorResponse. Вы можете использовать его в своем коде так:

(error: HttpErrorResponse) => { console.log('Message:' + error.error)});

Но сначала вам нужно добавить его в раздел импорта:

import { HttpClient, HttpErrorResponse } from '@angular/common/http';

Вы можете видеть, что я использовал error свойство, поскольку оно будет автоматически привязано к свойству Message класса Exception, которое было объявлено следующим образом:

public virtual string Message { get; }

Однако вы объявили другое свойство он называется status, который вы нигде не использовали, и он недоступен в вашем клиентском интерфейсе, поскольку между Angular и свойством, которое вы определили, нет никакой привязки свойства. Вы все еще можете использовать свойство Status класса HttpErrorResponse, но оно будет использовать код по умолчанию, который был возвращен со стороны сервера (скорее всего, это 500). В случае, если вы также хотите переопределить код состояния по умолчанию, вы можете вернуть другой тип ответа на стороне сервера, например, HttpResponseException.

РЕДАКТИРОВАТЬ: Кажется, ASP. NET Ядро не включает эквивалентный тип для HttpResponseException, на основе ссылки, которую я уже прикрепил:

In ASP. NET 4.x Web API, один из способов сделать это - использовать тип HttpResponseException. ASP. NET В ядре отсутствует эквивалентный тип.

Однако поддержку HttpResponseException можно добавить, выполнив следующие действия (опять-таки на основе статьи Microsoft *) 1036 *):

  1. Создать известный тип исключения с именем HttpResponseException:

    public class HttpResponseException : Exception
    {
        public int Status { get; set; } = 500;
    
        public object Value { get; set; }
    }
    
  2. Создать фильтр действий с именем HttpResponseExceptionFilter:

    public class HttpResponseExceptionFilter : IActionFilter, IOrderedFilter
    {
        public int Order { get; set; } = int.MaxValue - 10;
    
        public void OnActionExecuting(ActionExecutingContext context) { }
    
        public void OnActionExecuted(ActionExecutedContext context)
        {
            if (context.Exception is HttpResponseException exception)
            {
                context.Result = new ObjectResult(exception.Value)
                {
                    StatusCode = exception.Status,
                };
                context.ExceptionHandled = true;
            }
        }
    }
    
  3. В Startup.ConfigureServices добавьте фильтр действий в коллекцию фильтров:

    services.AddControllers(options =>
        options.Filters.Add(new HttpResponseExceptionFilter()));
    
2 голосов
/ 02 апреля 2020

Если исключение не является действительно необходимым, вы можете вернуть код состояния 400 и сообщение, используя BadRequest :

if (headerValues.Equals("abc")) {
    return BadRequest("Invalid String specified");
}
2 голосов
/ 02 апреля 2020

Вы генерируете исключение, которое обрабатывается обработчиком исключений C#, и оно будет возвращать только пользовательское сообщение об ошибке, указанное в этом обработчике.

Чтобы вернуть пользовательское сообщение, вам необходимо вернуться с http-кодом, таким как 4xx или 5xx.

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

Или вы можете вернуться с 2xx, и вам нужно проанализировать эту подписку или метод, например

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
    });
2 голосов
/ 02 апреля 2020

Вы явно перехватываете InvalidStringException в своем контроллере API. NET и возвращаете пользовательское сообщение? Если нет, ответом будет общий c HTTP 500 «Внутренняя ошибка сервера». Я бы предложил явно перехватить InvalidStringException в вашем контроллере API. NET и вернуть ответ 400 с вашим настраиваемым сообщением, например,

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

Когда происходит сценарий InvalidStringException, это вернет HTTP 400 ответ с "Указана неверная строка" в качестве тела ответа. Вы должны быть в состоянии зарегистрировать ошибку на стороне Angular, как вы делаете сейчас ...

2 голосов
/ 27 марта 2020

Объект ошибки, который получает ваше приложение Angular, должен быть экземпляром HttpErrorResponse

. Вы можете сделать что-то подобное для обработки ошибок http:

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");
}

1 голос
/ 05 апреля 2020

Как уже упоминали другие люди, вам нужно перехватить исключение и преобразовать его в соответствующий HTTP-ответ в вашем собственном коде.

Причина этого в том, что в противном случае ваше исключение обрабатывается ASP . NET Ядро, использующее конфигурация обработки исключений у вас есть, и она может варьироваться:

Со страницей исключений для разработчика

Обычно в процессе разработки у вас будет код:

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

Когда ваша среда Разработка , она открывает специальную страницу для разработчиков, чтобы увидеть информацию о необработанных исключениях. Это только , в этом случае вы получаете трассировку стека исключений, а также сообщение об исключении в теле ответа.

Без страницы исключений разработчика

И наоборот, если исключение страница выключена (обычно отключена для Production environment), в теле ответа вы увидите nothing .

Как исправить

Данное исключение обработка в ASP. NET Ядро является сквозной задачей, я бы не стал использовать try...catch везде, InvalidStringException необходимо преобразовать в HttpResponse.

Вместо этого я бы использовал IActionFilter или UseExceptionHandler, который является промежуточным ПО для обработки исключений :

Вот пример использования UseExceptionHandler в * Метод 1040 * в 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);
        }
    }));

Таким образом, ваш InvalidStringException обрабатывается глобально в вашем приложении, без явного try...catch. И вы можете выдать исключение в любом месте вашего кода, приведенный выше код перехватит исключение и правильно преобразует его в ответ HTTP с вашим собственным сообщением в качестве тела.

Также обратите внимание, потому что вы вызываете API из приложение Angular, так что скорее всего вам может понадобиться установить CORS на в вашем приложении API, если два приложения запускаются из разных источников.

Без CORS ваш HTTP-запрос от приложения Angular может завершиться ошибкой, прежде чем он достигнет вашего API. В этом случае статус ответа HTTP в вашем приложении Angular может быть undefined. И в вашей консоли вы могли видеть ошибки CORS.

0 голосов
/ 02 апреля 2020

вы можете использовать перехватчик http для создания общего обработчика ошибок для всех ошибок http в приложении angular, таким образом вы можете использовать оповещение, перенаправление на страницу входа в случае, если токен истек, перезаписать объект ошибки и многое другое, но вы все равно можете получить доступ к объекту ошибки на уровне компонента путем добавления обратного вызова для наблюдаемой ошибки.

Служба обработчика ошибок

@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);
      })
    );
  }
}

добавление перехватчика к поставщикам в модуле приложения

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

demo ?

MessageService относится к библиотеке компонентов primeng, вы можете использовать собственную структуру оповещений

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...