2009-02-24 13 views
199

私はJerseyを使ってJAX-RS(別名JSR-311)を学んでいます。私はsuccessfulyルートリソースを作成したとのパラメータで遊んでいます:JAX-RS/Jerseyエラー処理をカスタマイズする方法は?

@Path("/hello") 
public class HelloWorldResource { 

    @GET 
    @Produces("text/html") 
    public String get(
     @QueryParam("name") String name, 
     @QueryParam("birthDate") Date birthDate) { 

     // Return a greeting with the name and age 
    } 
} 

これは素晴らしい作品、および日付によって理解されている現在のロケールで任意の形式を扱う(String)コンストラクタ(のようなYYYY/MM/ddおよびmm/dd/YYYY)。しかし、私が無効であるか分からない価値を提供するなら、私は404応答を得ます。例えば

GET /hello?name=Mark&birthDate=X 

404 Not Found 

どのように私は、この動作をカスタマイズすることができますか?たぶん異なる応答コード(たぶん "400 Bad Request")?エラーの記録はどうですか?トラブルシューティングに役立つカスタムヘッダーに問題の説明(「不良な日付形式」)を追加することはできますか?または、5xxのステータスコードとともに、詳細とエラー応答全体を返しますか?

答えて

253

JAX-RSでエラー処理の動作をカスタマイズするには、いくつかの方法があります。ここに簡単な方法の3つがあります。

最初の方法は、WebApplicationExceptionを拡張するExceptionクラスを作成することです。

例:

public class NotAuthorizedException extends WebApplicationException { 
    public NotAuthorizedException(String message) { 
     super(Response.status(Response.Status.UNAUTHORIZED) 
      .entity(message).type(MediaType.TEXT_PLAIN).build()); 
    } 
} 

そして、これは、新たに例外単にあなたを作成スローする:WebApplicationExceptionはランタイムがあるので

@Path("accounts/{accountId}/") 
    public Item getItem(@PathParam("accountId") String accountId) { 
     // An unauthorized user tries to enter 
     throw new NotAuthorizedException("You Don't Have Permission"); 
} 

通知は、あなたがthrows節で例外を宣言する必要はありませんが例外。これにより、クライアントに401応答が返されます。

2番目の簡単な方法は、WebApplicationExceptionのインスタンスをコード内に直接作成することです。このアプローチは、独自のアプリケーション例外を実装する必要がない限り機能します。

例:

@Path("accounts/{accountId}/") 
public Item getItem(@PathParam("accountId") String accountId) { 
    // An unauthorized user tries to enter 
    throw new WebApplicationException(Response.Status.UNAUTHORIZED); 
} 

このコードはあまりにもクライアントに401を返します。

もちろん、これは単なる例です。必要に応じて例外をもっと複雑にすることができます。必要なHTTPレスポンスコードを生成できます。

もう1つの方法は、存在するException、おそらくObjectNotFoundExceptionを@Providerアノテーションで注釈されたExceptionMapperインターフェイスを実装する小さなラッパークラスでラップすることです。これは、ラップされたExceptionが発生した場合、ExceptionMapperで定義されたレスポンスコードを返すことをJAX-RSランタイムに伝えます。

+3

、スーパー(への呼び出し)、わずかに異なっていなければなりません:。。 スーパー(のResponse.Status(Status.UNAUTHORIZED) エンティティ(メッセージ).TYPE( "text/plainの")の構築()); 洞察に感謝します。 –

+0

彼の例ではメソッドが実行されないと思います。 – deamon

+61

質問に記載されているシナリオでは、入力値からDateオブジェクトのインスタンスを作成できないため、Jerseyは例外を発生させるため、例外をスローする機会はありません。ジャージーの例外を傍受する方法はありますか?ただし、1つのExceptionMapperインターフェイスがありますが、メソッドによってスローされた例外(この場合は取得)もインターセプトします。 –

11

明白な解決策の1つ:文字列を取り込み、自分自身に変換してください。こうすることで、必要な書式を定義したり、例外をキャッチしたり、送信されたエラーを再スローしたりカスタマイズしたりすることができます。 解析するために、SimpleDateFormatは正常に動作するはずです。

ハンドラをデータ型にフックする方法はありますが、この場合は単純なコードで少ししか必要ありません。

5

私はあまりにもStaxManのように、そのQueryParamをStringとして実装し、必要に応じて変換を処理します。ロケール固有の動作は、希望と期待される動作である場合、あなたは400 BAD REQUESTエラーを返すために、次を使用します

throw new WebApplicationException(Response.Status.BAD_REQUEST);

は、より多くのオプションのためのjavax.ws.rs.core.Response.StatusのJavaDocを参照してください。

26

ます。また、このようにそれを使用し、その後QueryParam注釈付きの変数

public class DateParam { 
    private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); 

    private Calendar date; 

    public DateParam(String in) throws WebApplicationException { 
    try { 
     date = Calendar.getInstance(); 
     date.setTime(format.parse(in)); 
    } 
    catch (ParseException exception) { 
     throw new WebApplicationException(400); 
    } 
    } 
    public Calendar getDate() { 
    return date; 
    } 
    public String format() { 
    return format.format(value.getTime()); 
    } 
} 

のための再利用可能なクラスを書くことができ:エラー処理は、この場合には自明であるが、400応答を投げる(

private @QueryParam("from") DateParam startDateParam; 
private @QueryParam("to") DateParam endDateParam; 
// ... 
startDateParam.getDate(); 

を)、このクラスを使用すると、ロギングなどを含む一般的なパラメータ処理を除外できます。

+0

Jerseyで(CXFから移行する)カスタムクエリのparamハンドラを追加しようとしていますが、これは私のやり方と著しく似ていますが、新しいプロバイダをインストール/作成する方法はわかりません。あなたの上のクラスは私にこれを示していません。私は、QueryParamのJodaTime DateTimeオブジェクトを使用していて、それらをデコードするプロバイダを持っていません。 それをサブクラス化してStringコンストラクタを与え、それを処理するのと同じくらい簡単ですか? –

+1

'java.util.Calendar'の代わりに' org.joda.time.DateTime'をラップする 'DateParam'のようなクラスを作成してください。 '@ QueryParam'では' DateTime'自体ではなく、それを使用します。 –

+1

Joda DateTimeを使用している場合、jerseyにはDateTimeParamが付属していますので、直接使用することができます。あなた自身で書く必要はありません。 https://github.com/dropwizard/dropwizard/blob/master/dropwizard-jersey/src/main/java/io/dropwizard/jersey/params/DateTimeParam.java – Srikanth

63
@Provider 
public class BadURIExceptionMapper implements ExceptionMapper<NotFoundException> { 

public Response toResponse(NotFoundException exception){ 

    return Response.status(Response.Status.NOT_FOUND). 
    entity(new ErrorResponse(exception.getClass().toString(), 
       exception.getMessage())). 
    build(); 
} 
} 

上記のクラスを作成します。これは404(NotFoundException)を処理し、ここでtoResponseメソッドでカスタム応答を与えることができます。同様に、カスタマイズされた応答を提供するためにマップする必要があるParamExceptionなどがあります。

+0

implements ExceptionMapper も一般的な例外のために使用できます – userRaj

+0

これは、JAX-RS ClientによってスローされたWebApplicationExceptionsも処理し、エラーの起点を隠します。カスタム例外(WebApplicationExceptionから派生したものではありません)を持っているか、完全な応答でWebApplicationsを投げてください。 JAX-RSクライアントがスローしたWebApplicationExceptionsは、コール時に直接処理する必要があります。処理されていない内部サーバーエラーですが、別のサービスの応答がサービスの応答として渡されます。 –

33

それがパラメータをアンマーシャリングに失敗したときジャージーそう一つの解決策は、例外のこれらのタイプを処理ExceptionMapperを作成することですcom.sun.jersey.api.ParamExceptionをスロー:

@Provider 
public class ParamExceptionMapper implements ExceptionMapper<ParamException> { 
    @Override 
    public Response toResponse(ParamException exception) { 
     return Response.status(Status.BAD_REQUEST).entity(exception.getParameterName() + " incorrect type").build(); 
    } 
} 
+0

Jerseyに登録するには、このマッパーをどこに登録すればよいですか? – Patricio

+1

@Providerアノテーションを追加するだけです。詳細はこちらをご覧ください:http://stackoverflow.com/questions/15185299/jax-rs-jersey-exceptionmappers-user-defined-exception –

4

@QueryParamドキュメントは

語ります

" The type T of the annotated parameter, field or property must either:

1) Be a primitive type
2) Have a constructor that accepts a single String argument
3) Have a static method named valueOf or fromString that accepts a single String argument (see, for example, Integer.valueOf(String))
4) Have a registered implementation of javax.ws.rs.ext.ParamConverterProvider JAX-RS extension SPI that returns a javax.ws.rs.ext.ParamConverter instance capable of a "from string" conversion for the type.
5) Be List, Set or SortedSet, where T satisfies 2, 3 or 4 above. The resulting collection is read-only. "

文字列形式のクエリパラメータを型Tに変換できないときに、ユーザーにどのような応答が送られるかを制御したい場合は、WebApplicationExceptionをスローすることができます。 Dropwizardには、以下のParamクラスが用意されています。

BooleanParam、DateTimeParam、IntParam、LongParam、LocalDateParam、NonEmptyStringParam、UUIDParam。 https://github.com/dropwizard/dropwizard/tree/master/dropwizard-jersey/src/main/java/io/dropwizard/jersey/params

Joda DateTimeが必要な場合は、Dropwizard DateTimeParamを使用してください。

上記のリストがあなたのニーズに合わない場合は、AbstractParamを拡張して独自のものを定義してください。解析メソッドをオーバーライドします。エラー応答本体を制御する必要がある場合は、エラーメソッドをオーバーライドします。この上のコーダヘイルから

良い記事はhttp://codahale.com/what-makes-jersey-interesting-parameter-classes/

import io.dropwizard.jersey.params.AbstractParam; 

import java.util.Date; 

import javax.ws.rs.core.Response; 
import javax.ws.rs.core.Response.Status; 

public class DateParam extends AbstractParam<Date> { 

    public DateParam(String input) { 
     super(input); 
    } 

    @Override 
    protected Date parse(String input) throws Exception { 
     return new Date(input); 
    } 

    @Override 
    protected Response error(String input, Exception e) { 
     // customize response body if you like here by specifying entity 
     return Response.status(Status.BAD_REQUEST).build(); 
    } 
} 

日で(文字列の引数)コンストラクタは廃止されています。 Java 8の場合はJava 8の日付クラスを使用します。それ以外の場合は、joda date timeを推奨します。

1

これは実際に正しい動作です。 Jerseyはあなたの入力用のハンドラを見つけようとし、提供された入力からオブジェクトを構築しようとします。この場合、コンストラクタに渡された値Xを持つ新しいDateオブジェクトを作成しようとします。これは無効な日付なので、Jerseyは404を返します。

誕生日を書き換えて誕生日を文字列として入力し、解析してみましょう。任意の例外マッピングメカニズム(いくつかあります)によって任意の例外をスローすることができます。あなたの例では

-5
abtrack class Responce 
{ 
private String message ; 
private int code ; 

public String getMessage(){ 
return this.message ; 
} 
public void setMessage(String message){ 
this.message =message ; 
} 


public String getCode(){ 
return this.code ; 
} 
public void setCode(String code){ 
this.code =code ; 
} 

} 

@XmlRootElement(name='MyResponce') 
class MyResponce extends Responce { 

} 

@Path("/hello") 
public class HelloWorldResource { 

    @GET 
    @Produces("text/html") 
    public MyResponce get(
     MyResponce myResponce = new MyResponce(); 
     @QueryParam("name") String name, 
     @QueryParam("birthDate") Date birthDate) throw WSException { 
      try { 
}catch(Exception) 
myResponce.setCode(400); 
myResponce.setMessage("Exception") 
    } 
return myResponce ; 

} 
関連する問題