如何在 angular 7 中获取 http 后端错误响应 body?

How to get http backend error response body in angular 7?

使用 Spring 引导作为我的后端,它向我的前端 angular 发送一些带有 json body 的错误响应。我设置了 angular http 拦截器来拦截 http 错误。但我无法从后端获得 json 错误响应。

打开 chrome 工具,我可以看到响应,当我从 angular 拦截器记录 http 错误时,错误 object 为空。

return next.handle(request)

      .pipe(tap(event => {
          // 
      }, (err: HttpErrorResponse) => {
        console.log(JSON.stringify(err));
      }));

我希望能够访问从 chrome 开发工具

复制的此响应的消息 属性
{timestamp: "2019-06-24T15:17:41.864+0000", status: 400, error: "Bad Request",…}
error: "Bad Request"
errors: [{codes: ["Min.examinerLogin.phoneNo", "Min.phoneNo", "Min.java.lang.String", "Min"],…}]
message: "Validation failed for object='examinerLogin'. Error count: 1"
path: "/api/public/letters"
status: 400
timestamp: "2019-06-24T15:17:41.864+0000"
trace: "org.springframework.web.bind.MethodArgumentNotVali"

我的拦截器代码是这样的:

    import {Injectable} from '@angular/core';
import {HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse, HttpResponse} from '@angular/common/http';
import { AuthService } from './auth.service';
import {Router} from '@angular/router';
import {Observable} from 'rxjs';
import {tap} from 'rxjs/operators';
import {MessageService} from 'primeng/api';

@Injectable({
  providedIn: 'root'
})

export class TokenInterceptor implements HttpInterceptor {

  constructor(
    private router: Router,
    private authenticationService: AuthService,
    private messageService: MessageService
    ) {}

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    return next.handle(request)

      .pipe(tap(event => {
        if (event instanceof HttpResponse) {
          // do stuff with response if you want
        }
      }, (err: HttpErrorResponse) => {
        console.log(JSON.stringify(err));
        if (err instanceof HttpErrorResponse) {

              this.messageService.add({
                severity: 'error',
                summary: 'Server Error', detail: err.error.message
              });
            }
      }));
  }
}

更新: 在我的 http 服务调用中,我修改了 headers 并将响应类型从默认 JSON 更改为 arrayBuffer 像这样:

    printLetter(letter: Letter) {
    const headers = new HttpHeaders();
    // @ts-ignore
    return this.http.post<any>(this.apiLetter, letter, {headers, responseType: 'arrayBuffer'});
  }

所以 Angular 期望 arrayBuffer 响应而不是 JSON 错误响应。现在我想知道响应类型是否可以是数组类型,这样可以预期 JSON 和 arrayBuffer

不要尝试将 err 转换为 HttpErrorResponse,在这种情况下只会给您带来麻烦。而是将其转换为 any。这是更正后的代码:

export class TokenInterceptor implements HttpInterceptor {

  constructor(
    private router: Router,
    private authenticationService: AuthService,
    private messageService: MessageService
    ) {}

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    return next.handle(request)

      .pipe(tap(event => {
        if (event instanceof HttpResponse) {
          // do stuff with response if you want
        }
      }, (err: any) => {
        console.log(JSON.stringify(err));
        if (err instanceof HttpErrorResponse) {

              this.messageService.add({
                severity: 'error',
                summary: 'Server Error', detail: err.error.message
              });
            }
      }));
  }
}

编辑:

也不要简单地向客户端发送spring异常错误。客户端一定不知道你在后端使用Spring。而是使用 @ControllerAdvice 异常处理程序 class 和 @ExceptionHandler 方法发回自定义错误消息,每个方法处理不同的错误。例如,未经身份验证的异常可能由这样的方法处理:

@ExceptionHandler(AuthenticationException.class)
public ResponseEntity<String> authenticationException(final AuthenticationException e) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    return new ResponseEntity<String>("You need to be logged in to do this", headers, HttpStatus.UNAUTHORIZED);
}

这将创建一个带有状态代码 401 和自定义消息的 HttpErrorResponse

编辑 2:

关于 spring boot 发送的错误对象的另一个警告。我发现 Spring 在错误响应中打包错误消息的方式不是很连贯。有时确实可以使用 error.error.message 检索错误消息,但有时只能通过从 error.error 获取消息来检索,在这种情况下它似乎仍然具有 属性 message只是它的值是undefined。为什么会这样我不知道,但我可以建议大家在使用消息之前将消息提取到消息变量中,并先检查一下 error.error 对象是否有一个 属性 调用message 如果是,检查消息是否未定义。如果不是,使用 message 的值,如果是,从 error.error.

获取

我知道这听起来很老套,但这是让这些东西发挥作用的唯一方法。不这样做并在 message 属性 上使用 String 方法,而实际上这个值是 "undefined" 因为 spring 或 angular 或任何决定放置消息的东西in error.error 反而会导致一些怪异的行为。例如,这个 if(errorMessage && errorMessage.message.includes('#redirect')) 导致整个 if-else 结构被简单地中断,而不是评估条件是真还是假。我通过在 if 和 else 括号之间添加 console.log("test) 语句来确认这种奇怪的行为。 None 的日志显示在浏览器中,此外还有我在 if-else 之后放置的任何日志结构也没有显示!都是因为我只是根据 angular 在 "undefined" 的消息上调用 .includes。所以没有 javascript 错误被抛出,而是神秘而无声地抛出退出 if-else 结构而不经过其中任何一个,而且之后它不执行任何日志。我认为这一定是 angular 中的错误或其他内容。

无论如何,就像说的那样:确定 error.error 对象是否有 message 属性 如果有,检查 angular 是否说它有一个未定义的值.您可以使用此代码来检索消息。

let errorMessage:any = (Object.prototype.hasOwnProperty.call(error.error, 'message') && error.error.message != undefined) ? error.error.message : error.error;

终于成功了...

来自此处的回答

所以我将这篇文章添加到我的 error.interceptor.ts 文件中,瞧

....
if (err.error instanceof Blob) {
   const reader: FileReader = new FileReader();
   reader.onloadend = () => {
   if (typeof reader.result === 'string') {
       const err1 = JSON.parse(reader.result);
       this.messageService.add({
          severity: 'error',
          summary: err1.error, detail: err1.message
       });
   }
 };
 reader.readAsText(err.error);
            }
....