Jersey Grizzly で始める JAX-RS 入門 〜STEP2〜

前回作成したサンプルを使って JAX-RS のパスのマッピングについて見ていきます。

blog1.mammb.com


Path の指定

JAX-RS では Resource クラスに @Path アノテーションを付けることで URL とのマッピングを指定します。

以下の ExampleResource を作成します。

package example.web.resource;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.PathSegment;
import javax.ws.rs.core.Response;
import java.io.InputStream;
import java.util.StringJoiner;

@Path("example")
public class ExampleResource {

    @Path("hello")
    @GET
    public String pathHello() {
        return "hello";
    }
}

メソッドに @Path を指定することでパスの階層を扱うことができます。クラスに付けた @Path("example") と メソッドに付けた @Path("hello") で、example/hello というパスでのリクエストが、作成したメソッドにルーティングされます。

@GET で HTTP GET に応答する指定となります。

アプリケーションを起動し、curl で GET リクエストを投げてみましょう。

$ curl 'http://localhost:8090/example/hello'
hello

hello という文字列が返却されました。

メソッド の指定

@POST を指定することで HTTP POST に応答するようになります。

以下のメソッドを追加しましょう。

    @POST
    public Response echoPost(InputStream is) {
        return Response.ok(is).build();
    }

引数で指定した InputStream からは HTTPリクエストのボディの内容を取得することができます。

アプリケーションを起動し、curl で -X オプションを指定して POST リクエストを投げてみましょう。

$ curl 'http://localhost:8090/example' -X POST -d 'HELLO'
HELLO

リクエストに付けた HELLO という文字列がそのまま返却されました。


メソッドの指定は HTTP メソッドの種類に応じたアノテーションが用意されています。

  • @javax.ws.rs.GET
  • @javax.ws.rs.PUT
  • @javax.ws.rs.POST
  • @javax.ws.rs.DELETE
  • @javax.ws.rs.HEAD

WebDAV などのメソッドを扱う場合は @HttpMethod でアノテートすることで拡張可能です。 例えばLOCK メソッドが必要な場合は以下のようにアノテーションを自作します。

@Target({ElementType.METHOD})
@Retention({RetentionPolicy.RUNTIME})
@HttpMethod("LOCK")
public @interface LOCK {}

QueryParam

@QueryParam で HTTPリクエストのクエリを取得することができます。

    @GET
    @Produces({MediaType.TEXT_PLAIN})
    public String queryParam(@QueryParam("name") String name,
                             @QueryParam("age") @DefaultValue("30") int age) {
        return name + " " + age;
    }

@Produces({MediaType.TEXT_PLAIN}) でレスポンスのコンテントタイプが Content-Type: text/plain となります。

@DefaultValue でデフォルト値を指定することができます。

$ curl 'http://localhost:8090/example?name=Thomas'
Thomas 30


フィールドにアノテーションを付けて取得することもできます。

    @QueryParam("fieldValue")
    private String fieldValue;

    @GET
    @Path("field")
    public Response queryParamField() {
        return Response.ok(fieldValue).build();
    }
$ curl 'http://localhost:8090/example/field?fieldValue=Who'
Who

@PathParam

@Path@PathParam で HTTPリクエストのパスを取得することができます。

REST の場合はこちらが一般的ですね。

    @GET
    @Path("path/{name}")
    public Response pathParam(@PathParam("name") String name) {
        return Response.ok(name).build();
    }

URLパスを {name} で指定して @PathParam で取得します。

$ curl 'http://localhost:8090/example/path/Eddie'
Eddie


以下のようにいくつも指定することができます。

    @GET
    @Path("path/{firstname}-{lastname}")
    public Response pathParams(@PathParam("firstname") String first,
            @PathParam("lastname") String last) {
        return Response.ok(first + " " + last).build();
    }

@PathParam で応答する名前で取得できます。

$ curl 'http://localhost:8090/example/path/Eddie-Vedder'
Eddie Vedder

パスの正規表現指定

パスは正規表現を使ってマッチさせることができます。

    @GET
    @Path("path/{id : \\d+}")
    public Response pathParamsRegexp(@PathParam("id") int id) {
        return Response.ok("id[" + id + "]").build();
    }

この例では数字にマッチさせ、id という名前で取得しています。

$ curl 'http://localhost:8090/example/path/9999'
id[9999]

@MatrixParam

MatrixParam はパスパラメータを ; で区切る形式です。

    @GET
    @Path("matrix")
    public Response matrixParam(@MatrixParam("author") String author,
            @MatrixParam("year") int year) {
        return Response.ok("MatrixParam[" + author + " " + year + "]").build();
    }

以下のように取得できます。

$ curl 'http://localhost:8090/example/matrix;author=Michael;year=2016'
MatrixParam[Michael 2016]

PathSegment

パスは PathSegment として取得し、後から値を取り出すこともできます。

    @GET
    @Path("matrix/{model}/{year}")
    public Response pathSegment(@PathParam("model") PathSegment car,
            @PathParam("year") int year) {
        return Response.ok("PathSegment[" +
            car.getMatrixParameters().getFirst("color") +
            "]").build();
    }
$ curl 'http://localhost:8090/example/matrix/e50;color=black/2016'
PathSegment[black]

また、以下のように @Context で UriInfo から PathSegment を操作することもできます。

    @GET
    @Path("matrix/{model}/{year}")
    public Response pathSegment(@Context UriInfo info) {
        PathSegment model = info.getPathSegments().get(1);
        // ・・・
    }

UriInfo からはパス文字列を取得することなども出来ます。

ヘッダ情報の取得

@HeaderParam で HTTPヘッダの値、@CookieParam で cookie の値が取得できます。

    @GET
    @Path("header")
    public Response header(@HeaderParam("X-Name") String name) {
        return Response.ok("header[" + name + "]").build();
    }

    @GET
    @Path("cookie")
    public Response cookie(@CookieParam("name") String name) {
        return Response.ok("cookie[" + name + "]").build();
    }
$ curl 'http://localhost:8090/example/header' -H 'X-Name: Joe'
header[Joe]

$ curl 'http://localhost:8090/example/cookie' -b 'name=cookie'
cookie[cookie]

@FormParam

フォームからPOSTしたパラメータは @FormParam で取得できます。

    @POST
    @Path("form")
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public Response form(@FormParam("number") int number) {
        return Response.ok("form[" + number + "]").build();
    }

@Consumes にて受け入れるコンテントタイプを指定しています。

以下のように取得できます。

$ curl 'http://localhost:8090/example/form' -X POST -d 'number=9999'
form[9999]

@BeanParam

JAX-RS 2.0 からパラメータが多い場合は @BeanParam で任意のBeanを指定して取得できます。

    @POST
    @Path("bean")
    public Response bean(@BeanParam Input in) {
        StringJoiner joiner = new StringJoiner(",", "[", "]");
        return Response.ok("BeanParam" +
                joiner.add(in.getFirstName())
                      .add(in.getLastName())
                      .add(in.getContentType()).toString()).build();
    }

    public static class Input {
        @FormParam("firstName")
        String firstName;
        @FormParam("lastName")
        String lastName;
        @HeaderParam("Content-Type")
        String contentType;

        public String getFirstName() { return firstName;}
        public String getLastName() { return lastName; }
        public String getContentType() { return contentType; }

        public void setFirstName(String firstName) { this.firstName = firstName;}
        public void setLastName(String lastName) { this.lastName = lastName; }
        public void setContentType(String contentType) { this.contentType = contentType; }
    }

Bean のフィールドに @FormParam のように指定してまとめて取得することができます。

$ curl 'http://localhost:8090/example/bean' -X POST -d 'firstName=F&lastName=L'
BeanParam[F,L,application/x-www-form-urlencoded]

モデルの作成

以下のような CustomerAddress の POJO を作成します。

package example.domain.model;

import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;

@XmlRootElement
public class Customer implements Serializable {

    private Long id;
    private String firstName;
    private String lastName;
    private Address address;

    public Customer(Long id, String firstName, String lastName, Address address) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.address = address;
    }

    public Long getId() { return id; }
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public Address getAddress() { return address; }

    public void setId(Long id) { this.id = id; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setAddress(Address address) { this.address = address; }
}

Customer は @XmlRootElement でアノテートしておきます。 JAX-RS で扱う場合にはルートとなるモデルに このアノテーションが必要です。

Customer から参照する Address を以下のように作成します。

package example.domain.model;

import java.io.Serializable;

public class Address implements Serializable {

    private String street;
    private String city;
    private String state;
    private String zip;
    private String country;

    public Address(String street, String city, String state, String zip, String country) {
        this.street = street;
        this.city = city;
        this.state = state;
        this.zip = zip;
        this.country = country;
    }

    public String getStreet() { return street; }
    public String getCity() { return city; }
    public String getState() { return state; }
    public String getZip() { return zip; }
    public String getCountry() { return country; }

    public void setStreet(String street) { this.street = street; }
    public void setCity(String city) { this.city = city; }
    public void setState(String state) { this.state = state; }
    public void setZip(String zip) { this.zip = zip; }
    public void setCountry(String country) { this.country = country;}

Resource の作成

作成したモデルを扱うリソースとして CustomerResource を作成しましょう。

package example.web.resource;

import example.domain.model.Address;
import example.domain.model.Customer;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("customers")
public class CustomerResource {

    @GET
    @Path("{id}")
    @Produces({MediaType.APPLICATION_JSON})
    public Customer get(@PathParam("id") Long id) {
        return new Customer(id, "Michael", " Stipe",
                new Address("1016", "Hollywood", "CA", "90038", "US"));
    }

}

簡単のため、新しい Customer を作成して返却しているだけです。

@Produces({MediaType.APPLICATION_JSON}) を指定し、戻り値を Customer として以下のURLでアクセスします。

Customer は @XmlRootElement でアノテートしてあるため、メソッドの戻り値としてそのまま指定すれば 、上記の例では自動的にJSON形式に変換されます。

作成したリソースにアクセスしてみましょう。

http://localhost:8090/customers/100

以下のようにJson形式で取得することができます。

f:id:Naotsugu:20160315233538p:plain


簡単ですね。

ここまでのソースはGithubを参照してください。

blog1.mammb.com