タスク:スタックトレースで一般的なHTTP 500 Internal Server Error
を受け取り、クライアント側で同じ恐ろしいスタックトレースを受け取る代わりに、カスタマイズしたメッセージに別のステータスコード(403
など)を表示したい、何が起こったのかは開発者にとってより明確になるでしょう。そして、例外についてのメッセージをユーザーに追加します。
ここに私のアプリケーションから変更されたいくつかのクラスがあります:
サーバーパーツ:
AppException.class
-すべてのサーバー応答例外(クライアントに返す前に)この例外に変換します。ちょっと標準エンティティクラス
public class AppException extends WebApplicationException {
Integer status;
/** application specific error code */
int code;
/** link documenting the exception */
String link;
/** detailed error description for developers */
String developerMessage;
public AppException(int status, int code, String message, String developerMessage, String link) {
super(message);
this.status = status;
this.code = code;
this.developerMessage = developerMessage;
this.link = link;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getDeveloperMessage() {
return developerMessage;
}
public void setDeveloperMessage(String developerMessage) {
this.developerMessage = developerMessage;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public AppException() {
}
public AppException(String message) {
super("Something went wrong on the server");
}
}
ÀppExceptionMapper.class
-AppExceptionをJAX-RSランタイムにマップします。標準の例外ではなく、クライアントがAppExceptionを受け取ります。
@Provider
public class AppExceptionMapper implements ExceptionMapper<AppException> {
@Override
public Response toResponse(AppException exception) {
return Response.status(403)
.entity("toResponse entity").type("text/plain").build();
}
}
ApplicationService.class
-AppExceptionをスローするサービスクラス
@Path("/applications")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public interface ApplicationService {
@DELETE
@Path("/deleteById")
void deleteById(@NotNull Long id) throws AppException;
}
クライアントパーツ:
ErrorHandlingFilter.class
-AppExceptionの私の応答キャッチャー。ここでは、ステータスに応じて、各Response例外を別の例外に変換したいと思います。
@Provider
public class ErrorHandlingFilter implements ClientResponseFilter {
private static ObjectMapper _MAPPER = new ObjectMapper();
@Override
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
if (responseContext.getStatus() != Response.Status.OK.getStatusCode()) {
if(responseContext.hasEntity()) {
Error error = _MAPPER.readValue(responseContext.getEntityStream(), Error.class);
String message = error.getMessage();
Response.Status status = Response.Status.fromStatusCode(responseContext.getStatus());
AppException clientException;
switch (status) {
case INTERNAL_SERVER_ERROR:
clientException = new PermissionException(message);
break;
case NOT_FOUND:
clientException = new MyNotFoundException(message);
break;
default:
clientException = new WhatEverException(message);
}
throw clientException;
}
}
}
}
PermissionException.class
-500のステータスコードが付いている場合、AppExceptionを変換したい場合の例外。
public class PermissionException extends AppException{
public PermissionException(String message) {
super("403 - Forbidden. You dont have enough rights to delete this Application");
}
Integer status;
/** application specific error code */
int code;
/** link documenting the exception */
String link;
/** detailed error description for developers */
String developerMessage;
public PermissionException(int status, int code, String message, String developerMessage, String link) {
super(message);
this.status = status;
this.code = code;
this.developerMessage = developerMessage;
this.link = link;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getDeveloperMessage() {
return developerMessage;
}
public void setDeveloperMessage(String developerMessage) {
this.developerMessage = developerMessage;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public PermissionException() {}
}
ApplicationPresenter.class
-UIロジックの一部。ErrorHandlingFilterによってスローされたPermissionExceptionで何かを実行します。
@SpringPresenter
public class ApplicationPresenter implements ApplicationView.Observer {
@Resource
private ApplicationService applicationService;
@Resource
private UiEnvironment uiEnvironment;
@Override
public void deleteSelectedApplication(BeanItemGrid<Application> applicationGrid) {
try {
applicationService.deleteById(applicationGrid.getSelectedItem().getId());
} catch (PermissionException e) {
e.printStackTrace();
e.getMessage();
} catch (AppException e2) {
}
}
}
どうすれば問題を解決できますか?標準500 InternalErrorException.
をまだ受け取っています
質問全体がもう一度更新されました!
ExceptionMapperがある場合、HTTPリクエストでリソースメソッドが呼び出されたときに、例外を自分でキャッチするのではなく、フレームワークでキャッチします。
エラー処理を実行する適切な方法は、特定の(または一般的な)例外の場合にどの応答が返されるかを知っているExceptionMapper
インスタンスを登録することです。
@Provider
public class PermissionExceptionHandler implements ExceptionMapper<PermissionException>{
@Override
public Response toResponse(PermissionException ex){
//You can place whatever logic you need here
return Response.status(403).entity(yourMessage).build();
}
}
詳細については、私の他の答えを見てください: https://stackoverflow.com/a/23858695/25888
ここでは別のアプローチをとっています。メインのJavaメソッドで突堤サーバーを起動するときにこれを試すことができます
public static void main(String[] args) throws UnknownHostException, JSONException, IOException, Exception {
MyMain myMain = new MyMain();
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
Server jettyServer = new Server(5550);
jettyServer.setHandler(context);
context.setErrorHandler(new ErrorHandler());
// default error handler for resources out of "context" scope
jettyServer.addBean(new ErrorHandler());
ServletHolder jerseyServlet = context.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/*");
jerseyServlet.setInitOrder(0);
// Tells the Jersey Servlet which REST service/class to load.
jerseyServlet.setInitParameter("jersey.config.server.provider.classnames",
ControllerInn.class.getCanonicalName() );
try {
jettyServer.start();
jettyServer.join();
} catch (Exception ex) {
Logger.getLogger(ControllerInn.class.getName()).log(Level.SEVERE, null, ex);
} finally {
jettyServer.destroy();
}
}
/**
* Dummy error handler that disables any error pages or jetty related messages and returns our
* ERROR status JSON with plain HTTP status instead. All original error messages (from our code) are preserved
* as they are not handled by this code.
*/
static class ErrorHandler extends ErrorPageErrorHandler {
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
response.getWriter()
.append("{\"message\":\"HTTP ERROR ")
.append(String.valueOf(response.getStatus()))
.append("\"}");
}
}
だからあなたはこのような出力を得ることができます
{"message":"HTTP ERROR 500"}
ここ から参照できます
これは ジャージーの例 ですが、必要な情報は ここ から抽出できます。私は例外をスローし、最後にこの例外を必要な応答にマップするだけです。
例外をスローして、次のressourceメソッドがあるとしましょう:
@Path("items/{itemid}/")
public Item getItem(@PathParam("itemid") String itemid) {
Item i = getItems().get(itemid);
if (i == null) {
throw new CustomNotFoundException("Item, " + itemid + ", is not found");
}
return i;
}
例外クラスを作成します。
public class CustomNotFoundException extends WebApplicationException {
/**
* Create a HTTP 404 (Not Found) exception.
*/
public CustomNotFoundException() {
super(Responses.notFound().build());
}
/**
* Create a HTTP 404 (Not Found) exception.
* @param message the String that is the entity of the 404 response.
*/
public CustomNotFoundException(String message) {
super(Response.status(Responses.NOT_FOUND).
entity(message).type("text/plain").build());
}
}
次に、例外マッパーを追加します。
@Provider
public class EntityNotFoundMapper implements ExceptionMapper<CustomNotFoundException> {
public Response toResponse(CustomNotFoundException ex) {
return Response.status(404).
entity("Ouchhh, this item leads to following error:" + ex.getMessage()).
type("text/plain").
build();
}
}
最後に、例外マッパーを登録して、アプリケーションで使用できるようにする必要があります。ここにいくつかの擬似コードがあります:
register(new EntityNotFoundMapper());
//or
register(EntityNotFoundMapper.class);
上記で正しく提案されているように、ExceptionMapper
を実装した今、理想的な方法は、フレームワークに例外をキャッチさせることです。ただし、実行している現象の概要を説明する重要なポイントの1つ:キャッチされない例外を処理する必要がある場合は、Exception
を実装するExceptionMapper
を実装し、Throwable
にマップする必要があります。
public class UncaughtExcep implements ExceptionMapper<Throwable>{
@Override
public Response toResponse(Throwable e){
}
}
あなたのクラスWhatEverException
がそれに応えると仮定します。そうでない場合は、実装することをお勧めします