サーチ…


備考

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 。この場合、すべてのリソースメソッドが圧縮を使用することになります。

グローバルフィルタは常に実行されるため、名前バインディングアノテーションを持つリソースメソッドであっても注意が必要です。

ドキュメンテーション



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow