From a63916b08e6a74fe95cbd5a5683ffd8fac0f10cc Mon Sep 17 00:00:00 2001 From: gbrodman Date: Thu, 12 Oct 2023 14:03:12 -0400 Subject: [PATCH] Refine error handling in RequestHandler and the console slightly (#2177) If we don't explicitly handle random unexpected exceptions, the error that the front end receives includes a big ole stacktrace, which is unhelpful for regular users and possibly bad to expose. Instead, we should provide a vague "something went wrong" message. Separately, we can create a default SnackBar options and use that (we want it longer than 1.5 seconds because that's pretty short). --- console-webapp/src/app/app.module.ts | 2 ++ .../src/app/registrar/registrar.service.ts | 4 +--- .../app/settings/contact/contact.component.ts | 8 ++----- .../settings/security/security.component.ts | 4 +--- .../src/app/settings/whois/whois.component.ts | 5 ++-- .../app/shared/services/userData.service.ts | 4 +--- console-webapp/src/app/snackbar.module.ts | 24 +++++++++++++++++++ .../registry/request/RequestHandler.java | 4 ++++ 8 files changed, 37 insertions(+), 18 deletions(-) create mode 100644 console-webapp/src/app/snackbar.module.ts diff --git a/console-webapp/src/app/app.module.ts b/console-webapp/src/app/app.module.ts index deff78208..db4582a40 100644 --- a/console-webapp/src/app/app.module.ts +++ b/console-webapp/src/app/app.module.ts @@ -48,6 +48,7 @@ import { DomainsWidgetComponent } from './home/widgets/domains-widget.component' import { SettingsWidgetComponent } from './home/widgets/settings-widget.component'; import { UserDataService } from './shared/services/userData.service'; import WhoisComponent from './settings/whois/whois.component'; +import { SnackBarModule } from './snackbar.module'; @NgModule({ declarations: [ @@ -79,6 +80,7 @@ import WhoisComponent from './settings/whois/whois.component'; FormsModule, HttpClientModule, MaterialModule, + SnackBarModule, ], providers: [ BackendService, diff --git a/console-webapp/src/app/registrar/registrar.service.ts b/console-webapp/src/app/registrar/registrar.service.ts index 4d94742ed..4c9c29340 100644 --- a/console-webapp/src/app/registrar/registrar.service.ts +++ b/console-webapp/src/app/registrar/registrar.service.ts @@ -89,8 +89,6 @@ export class RegistrarService implements GlobalLoader { } loadingTimeout() { - this._snackBar.open('Timeout loading registrars', undefined, { - duration: 1500, - }); + this._snackBar.open('Timeout loading registrars'); } } diff --git a/console-webapp/src/app/settings/contact/contact.component.ts b/console-webapp/src/app/settings/contact/contact.component.ts index 938fe8743..421b6966a 100644 --- a/console-webapp/src/app/settings/contact/contact.component.ts +++ b/console-webapp/src/app/settings/contact/contact.component.ts @@ -129,9 +129,7 @@ export class ContactDetailsDialogComponent { operationObservable.subscribe({ complete: this.onCloseCallback.bind(this), error: (err: HttpErrorResponse) => { - this._snackBar.open(err.error, undefined, { - duration: 1500, - }); + this._snackBar.open(err.error); }, }); } @@ -175,9 +173,7 @@ export default class ContactComponent { if (confirm(`Please confirm contact ${contact.name} delete`)) { this.contactService.deleteContact(contact).subscribe({ error: (err: HttpErrorResponse) => { - this._snackBar.open(err.error, undefined, { - duration: 1500, - }); + this._snackBar.open(err.error); }, }); } diff --git a/console-webapp/src/app/settings/security/security.component.ts b/console-webapp/src/app/settings/security/security.component.ts index c612603d6..c4c9c739f 100644 --- a/console-webapp/src/app/settings/security/security.component.ts +++ b/console-webapp/src/app/settings/security/security.component.ts @@ -64,9 +64,7 @@ export default class SecurityComponent { this.resetDataSource(); }, error: (err: HttpErrorResponse) => { - this._snackBar.open(err.error, undefined, { - duration: 1500, - }); + this._snackBar.open(err.error); }, }); this.cancel(); diff --git a/console-webapp/src/app/settings/whois/whois.component.ts b/console-webapp/src/app/settings/whois/whois.component.ts index 04c2f670b..a0891a6b2 100644 --- a/console-webapp/src/app/settings/whois/whois.component.ts +++ b/console-webapp/src/app/settings/whois/whois.component.ts @@ -61,9 +61,8 @@ export default class WhoisComponent { this.resetDataSource(); }, error: (err: HttpErrorResponse) => { - this._snackBar.open(err.error, undefined, { - duration: 1500, - }); + this._snackBar.open(err.error); + this.loading = false; }, }); this.cancel(); diff --git a/console-webapp/src/app/shared/services/userData.service.ts b/console-webapp/src/app/shared/services/userData.service.ts index b1cd107fe..23dc0a79d 100644 --- a/console-webapp/src/app/shared/services/userData.service.ts +++ b/console-webapp/src/app/shared/services/userData.service.ts @@ -49,8 +49,6 @@ export class UserDataService implements GlobalLoader { } loadingTimeout() { - this._snackBar.open('Timeout loading user data', undefined, { - duration: 1500, - }); + this._snackBar.open('Timeout loading user data'); } } diff --git a/console-webapp/src/app/snackbar.module.ts b/console-webapp/src/app/snackbar.module.ts new file mode 100644 index 000000000..72fc4219e --- /dev/null +++ b/console-webapp/src/app/snackbar.module.ts @@ -0,0 +1,24 @@ +// Copyright 2023 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { NgModule } from '@angular/core'; +import { MAT_SNACK_BAR_DEFAULT_OPTIONS } from '@angular/material/snack-bar'; + +/** Provides a default set of options for the snack bar. */ +@NgModule({ + providers: [ + { provide: MAT_SNACK_BAR_DEFAULT_OPTIONS, useValue: { duration: 5000 } }, + ], +}) +export class SnackBarModule {} diff --git a/core/src/main/java/google/registry/request/RequestHandler.java b/core/src/main/java/google/registry/request/RequestHandler.java index 93fb1d010..954f67598 100644 --- a/core/src/main/java/google/registry/request/RequestHandler.java +++ b/core/src/main/java/google/registry/request/RequestHandler.java @@ -17,6 +17,7 @@ package google.registry.request; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8; import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; +import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; import static javax.servlet.http.HttpServletResponse.SC_METHOD_NOT_ALLOWED; import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; @@ -162,6 +163,9 @@ public class RequestHandler { } catch (HttpException e) { e.send(rsp); success = false; + } catch (Exception e) { + rsp.setStatus(SC_INTERNAL_SERVER_ERROR); + rsp.getWriter().write("Internal server error, please try again later"); } finally { requestMetrics.record( new Duration(startTime, clock.nowUtc()),