java-ee
Java RESTful Webサービス(JAX-RS)
サーチ…
備考
W3C標準として指定されているSOAPやWSスタックとは異なり、RESTは実際にWebベースのインタフェースを設計して使用する一連の原則です。 REST / RESTfulアプリケーションは、他の標準に大きく依存しています。
HTTP
URI, URL
XML, JSON, HTML, GIF, JPEG, and so forth (resource representations)
JAX-RS(RESTful Webサービス用のJava API)の役割は、RESTfulなサービスの構築をサポートするAPIを提供することです。しかし、JAX-RSはこれを行うための単なる方法です 。 RESTfulなサービスは、Javaでは(実際に)他の多くのプログラミング言語で実装することができます。
シンプルなリソース
まず、JAX-RSアプリケーションのためには、すべてのリソースを利用できるベースURIを設定する必要があります。そのためにjavax.ws.rs.core.Application
クラスを拡張し、 javax.ws.rs.ApplicationPath
アノテーションで注釈を付ける必要がjavax.ws.rs.ApplicationPath
。注釈は、ベースURIを定義する文字列引数を受け入れます。
@ApplicationPath(JaxRsActivator.ROOT_PATH)
public class JaxRsActivator extends Application {
/**
* JAX-RS root path.
*/
public static final String ROOT_PATH = "/api";
}
リソースとは、 @Path
アノテーションでアノテーションされた単純なPOJOクラスです。
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@Path("/hello")
public class HelloWorldResource {
public static final String MESSAGE = "Hello StackOverflow!";
@GET
@Produces("text/plain")
public String getHello() {
return MESSAGE;
}
}
HTTP GET
要求が/hello
に送信されると、リソースはHello StackOverflow!
応答しHello StackOverflow!
メッセージ。
GETメソッドの型
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@Path("/hello")
public class HelloWorldResource {
public static final String MESSAGE = "Hello World!";
@GET
@Produces("text/plain")
public String getHello() {
return MESSAGE;
}
@GET
@Path("/{letter}")
@Produces("text/plain")
public String getHelloLetter(@PathParam("letter") int letter){
if (letter >= 0 && letter < MESSAGE.length()) {
return MESSAGE.substring(letter, letter + 1);
} else {
return "";
}
}
}
パラメータなしのGET
はすべてのコンテンツ( "Hello World!")を与え、pathパラメータを持つGET
はその文字列から特定の文字を取り出します。
いくつかの例:
$ curl http://localhost/hello Hello World!
$ curl http://localhost/hello/0 H
$ curl http://localhost/hello/4 o
注:メソッドタイプの注釈(例えば、上記の@GET
)を@GET
と、リクエストメソッドはデフォルトでGETリクエストハンドラになります。
DELETEメソッド
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
@Path("hello")
public class HelloWorldResource {
private String message = "Hello StackOverflow!";
@GET
@Produces("text/plain")
public String getHello() {
return message;
}
@DELETE
public Response deleteMessage() {
message = null;
return Response.noContent().build();
}
}
カールで消費する:
$ curl http://localhost/hello
Hello StackOverflow!
$ curl -X "DELETE" http://localhost/hello
$ curl http://localhost/hello
null
POSTメソッド
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
@Path("hello")
public class HelloWorldResource {
@POST
@Path("/receiveParams")
public Response receiveHello(@FormParam("name") String name, @FormParam("message") String message) {
//process parameters
return Response.status(200).build();
}
@POST
@Path("/saveObject")
@Consumes("application/json")
public Response saveMessage(Message message) {
//process message json
return Response.status(200).entity("OK").build();
}
}
最初のメソッドは、キャプチャされた入力パラメータを送信することによって、HTMLフォームのサブミットを介して呼び出すことができます。フォーム提出アクションは、
/hello/receiveParams
2番目のメソッドは、getters / setterを持つMessage POJOを必要とします。すべてのRESTクライアントはJSON入力をこのメソッドで呼び出すことができます。
{"sender":"someone","message":"Hello SO!"}
POJOはJSONと同じプロパティを持っていなければなりません。
public class Message {
String sender;
String message;
public String getSender() {
return sender;
}
public void setSender(String sender) {
this.sender = sender;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
例外マッパー
@Provider
public class IllegalArgumentExceptionMapper implements ExceptionMapper<IllegalArgumentException> {
@Override
public Response toResponse(IllegalArgumentException exception) {
return Response.serverError().entity("Invalid input: " + exception.getMessage()).build();
}
}
この例外マッパーは、アプリケーションでスローされたすべてのIllegalArgumentExceptionsを捕捉し、スタックトレースの代わりにクリアなメッセージを表示します。
UriInfo
ユーザエージェントがあなたのリソースにアクセスするために使用したURIに関する情報を取得するために、 @Context
パラメータ注釈をUriInfo
パラメータとともに使用できます。 UriInfo
オブジェクトには、URIのさまざまな部分を取得するために使用できるメソッドがいくつかあります。
//server is running on https://localhost:8080,
// webapp is at /webapp, servlet at /webapp/servlet
@Path("class")
class Foo {
@GET
@Path("resource")
@Produces(MediaType.TEXT_PLAIN)
public Response getResource(@Context UriInfo uriInfo) {
StringBuilder sb = new StringBuilder();
sb.append("Path: " + uriInfo.getPath() + "\n");
sb.append("Absolute Path: " + uriInfo.getAbsolutePath() + "\n");
sb.append("Base URI: " + uriInfo.getBaseUri() + "\n");
sb.append("Request URI: " + uriInfo.getRequestUri() + "\n");
return Response.ok(sb.toString()).build();
}
}
https://localhost:8080/webapp/servlet/class/resource
へのGETの出力:
Path: class/resource
Absolute Path: https://localhost:8080/webapp/servlet/class/resource#
Base URI: https://localhost:8080/webapp/servlet/
Request URI: https://localhost:8080/webapp/servlet/class/resource
サブリソース
組織やその他の理由から、トップレベルのリソースにこのようなサブリソースが返されるようにするのが理にかなっています。 (あなたのサブリソースは内部クラスである必要はありません)
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@Path("items")
public class ItemsResource {
@Path("{id}")
public String item(@PathParam("id") String id) {
return new ItemSubResource(id);
}
public static class ItemSubResource {
private final String id;
public ItemSubResource(String id) {
this.id = id;
}
@GET
@Produces("text/plain")
public Item item() {
return "The item " + id;
}
}
}
カスタムパラメータコンバーター
これは、JAX-RSエンドポイント用のカスタムパラメータコンバータを実装する方法の例です。この例では、Java 8のjava.time
ライブラリの2つのクラスを示しています。
@Provider
public class ParamConverters implements ParamConverterProvider {
@Override
public <T> ParamConverter<T> getConverter(Class<T> rawType,
Type genericType,
Annotation[] annotations)
{
if (rawType == LocalDate.class)
return (ParamConverter<T>) new ParamConverter<LocalDate>() {
@Override
public LocalDate fromString(String value) {
return LocalDate.parse(value);
}
@Override
public String toString(LocalDate value) {
return null;
}
};
else if (rawType == MonthDay.class)
return (ParamConverter<T>) new ParamConverter<MonthDay>() {
@Override
public MonthDay fromString(String value) {
int[] ddmm = Arrays.stream(value.split("/"))
.mapToInt(Integer::parseInt)
.toArray();
return MonthDay.of(ddmm[1], ddmm[0]);
}
@Override
public String toString(MonthDay value) {
return null;
}
};
return null;
}
}
名前バインディング
ネームバインディングは、特定のフィルタまたはインターセプタが特定のリソースメソッドに対してのみ実行されることをJAX-RSランタイムに伝える概念です。フィルタまたはインターセプタが特定のリソースメソッドのみに限定されている場合は、 名前にバインドされています。そのような制限を持たないフィルタとインターセプタは、 グローバルと呼ばれます。
名前バインディングアノテーションの定義
@NameBinding
アノテーションを使用して、フィルタまたはインターセプタをリソースメソッドに割り当てることができます。この注釈は、プロバイダおよびリソースメソッドに適用される他のユーザ実装注釈のメタ注釈として使用されます。次の例を参照してください。
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface Compress {}
上記の例では、新しい@Compress
アノテーションが定義されています。このアノテーションは、 @Compress
アノテーションが付けられているため、名前バインディングアノテーション@NameBinding
。 @Compress
アノテーションは、フィルターとインターセプターをエンドポイントにバインドするために使用できます。
フィルタまたはインターセプタをエンドポイントにバインドする
GZIP圧縮を実行するインターセプタがあり、そのインターセプタをリソースメソッドにバインドしたいとします。これを行うには、次のように、リソースメソッドとインターセプタの両方に注釈を付けます。
@Compress
public class GZIPWriterInterceptor implements WriterInterceptor {
@Override
public void aroundWriteTo(WriterInterceptorContext context)
throws IOException, WebApplicationException {
final OutputStream outputStream = context.getOutputStream();
context.setOutputStream(new GZIPOutputStream(outputStream));
context.proceed();
}
}
@Path("helloworld")
public class HelloWorldResource {
@GET
@Produces("text/plain")
public String getHello() {
return "Hello World!";
}
@GET
@Path("too-much-data")
@Compress
public String getVeryLongString() {
String str = ... // very long string
return str;
}
}
@Compress
はリソースメソッドgetVeryLongString()
とインターセプターGZIPWriterInterceptor
適用されます。インターセプタは、そのようなアノテーションを持つリソースメソッドが実行される場合にのみ実行されます。
上記の例では、インターセプタはgetVeryLongString()
メソッドに対してのみ実行されます。メソッドgetHello()
に対してインターセプタは実行されません。この例では、理由はおそらく明らかです。長いデータのみを圧縮したいので、 "Hello World!"
という短いレスポンスを圧縮する必要はありません"Hello World!"
。
名前バインディングは、リソースクラスに適用できます。この例では、 HelloWorldResource
@Compress
アノテーションが付けられ@Compress
。この場合、すべてのリソースメソッドが圧縮を使用することになります。
グローバルフィルタは常に実行されるため、名前バインディングアノテーションを持つリソースメソッドであっても注意が必要です。