Szukaj…


Uwagi

W przeciwieństwie do SOAP i stosu WS, które są określone jako standardy W3C, REST jest naprawdę zbiorem zasad projektowania i korzystania z interfejsu internetowego. Aplikacje REST / RESTful w dużej mierze opierają się na innych standardach:

HTTP
URI, URL
XML, JSON, HTML, GIF, JPEG, and so forth (resource representations)

Rolą JAX-RS (Java API for RESTful Web Services) jest dostarczanie interfejsów API, które wspierają budowanie usług RESTful. JAX-RS to jednak tylko jeden ze sposobów . Usługi RESTful mogą być implementowane na inny sposób w Javie i (rzeczywiście) w wielu innych językach programowania.

Prosty zasób

Przede wszystkim dla aplikacji JAX-RS należy ustawić podstawowy identyfikator URI, z którego będą dostępne wszystkie zasoby. W tym celu klasa javax.ws.rs.core.Application musi zostać rozszerzona i opatrzona adnotacjami za pomocą adnotacji javax.ws.rs.ApplicationPath . Adnotacja akceptuje argument łańcuchowy, który definiuje podstawowy identyfikator URI.

@ApplicationPath(JaxRsActivator.ROOT_PATH)
public class JaxRsActivator extends Application {

    /**
     * JAX-RS root path.
     */
    public static final String ROOT_PATH = "/api";

}

Zasoby to proste klasy POJO opatrzone adnotacją @Path .

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;
    }
}

Gdy żądanie HTTP GET jest wysyłane do /hello , zasób odpowiada komunikatem Hello StackOverflow! wiadomość.

Typy metod 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 bez parametru daje całą zawartość („Witaj świecie!”), A GET z parametrem ścieżki daje określoną literę z tego ciągu.

Kilka przykładów:

$ curl http://localhost/hello
Hello World!
$ curl http://localhost/hello/0
H
$ curl http://localhost/hello/4
o

Uwaga: jeśli @GET adnotację typu metody (np. @GET powyżej), domyślną metodą żądania jest metoda obsługi żądań GET.

Metoda 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();
    }
}

Spożywać z curl:

$ curl http://localhost/hello
Hello StackOverflow!

$ curl -X "DELETE" http://localhost/hello


$ curl http://localhost/hello
null

Metoda 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();
    }
}

Pierwszą metodę można wywołać przez przesłanie formularza HTML, wysyłając przechwycone parametry wejściowe. Formularz przesłania działania powinien wskazywać -

/hello/receiveParams

Druga metoda wymaga komunikatu POJO z modułami pobierającymi / ustawiającymi. Każdy klient REST może wywołać tę metodę z danymi wejściowymi JSON jako -

{"sender":"someone","message":"Hello SO!"}

POJO powinien mieć tę samą właściwość co JSON, aby serializacja działała.

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;
    }
}

Maper wyjątków

@Provider
public class IllegalArgumentExceptionMapper implements ExceptionMapper<IllegalArgumentException> {
        
  @Override
  public Response toResponse(IllegalArgumentException exception) {
    return Response.serverError().entity("Invalid input: " + exception.getMessage()).build();
  }
}

Ten program odwzorowujący wyjątki przechwytuje wszystkie wyjątki IllegalArgumentException zgłoszone w aplikacji i wyświetla użytkownikowi wyraźny komunikat zamiast śledzenia stosu.

UriInfo

Aby uzyskać informacje o identyfikatorze URI, którego agent użytkownika użył do uzyskania dostępu do Twojego zasobu, możesz użyć adnotacji parametru @Context z parametrem UriInfo . Obiekt UriInfo ma kilka metod, za pomocą których można uzyskać różne części identyfikatora 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();
    }
}

wyjście GET do https://localhost:8080/webapp/servlet/class/resource :

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

SubResources

Czasami z przyczyn organizacyjnych lub z innych powodów sensowne jest, aby zasób najwyższego poziomu zwrócił podrzędny zasób, który wyglądałby tak. (Twój zasób nie musi być klasą wewnętrzną)

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;
        }
    }
}

Niestandardowe konwertery parametrów

To jest przykład implementacji konwerterów parametrów niestandardowych dla punktów końcowych JAX-RS. Przykład pokazuje dwie klasy z biblioteki java.time Java 8.

@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;
  }
}

Wiązanie nazwy

Wiązanie nazw to koncepcja, która pozwala powiedzieć środowisku wykonawczemu JAX-RS, że określony filtr lub przechwytywacz zostanie wykonany tylko dla określonej metody zasobu. Kiedy filtr lub przechwytywacz jest ograniczony tylko do określonej metody zasobu, mówimy, że jest on związany z nazwą . Filtry i przechwytywacze, które nie mają takiego ograniczenia, nazywane są globalnymi .

Definiowanie adnotacji wiążącej nazwę

Filtry lub przechwytywacze można przypisać do metody zasobu za pomocą adnotacji @NameBinding . Ta adnotacja jest używana jako adnotacja meta dla innych adnotacji zaimplementowanych przez użytkownika, które są stosowane do dostawców i metod zasobów. Zobacz następujący przykład:

@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface Compress {}

W powyższym przykładzie zdefiniowano nową adnotację @Compress która jest adnotacją wiążącą nazwę, ponieważ jest opatrzona adnotacją @NameBinding . Adnotacji @Compress można użyć do powiązania filtrów i przechwytywacza z punktami końcowymi.

Wiązanie filtra lub przechwytywacza z punktem końcowym

Załóżmy, że masz przechwytywacz, który wykonuje kompresję GZIP i chcesz powiązać taki przechwytywacz z metodą zasobów. Aby to zrobić, należy opisać zarówno metodę zasobu, jak i przechwytywacz w następujący sposób:

@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 jest stosowany w metodzie zasobów getVeryLongString() i w interceptorze GZIPWriterInterceptor . Przechwytywacz zostanie wykonany tylko wtedy, gdy zostanie wykonana jakakolwiek metoda zasobu z taką adnotacją.

W powyższym przykładzie przechwytywacz zostanie wykonany tylko dla metody getVeryLongString() . Przechwytywacz nie zostanie wykonany dla metody getHello() . W tym przykładzie przyczyna jest prawdopodobnie jasna. Chcemy kompresować tylko długie dane i nie musimy kompresować krótkiej odpowiedzi "Hello World!" .

Powiązanie nazwy można zastosować w klasie zasobów. W przykładzie HelloWorldResource będzie opatrzony adnotacją @Compress . Oznaczałoby to, że wszystkie metody zasobów będą w tym przypadku używać kompresji.

Pamiętaj, że filtry globalne są wykonywane zawsze, więc nawet w przypadku metod zasobów, które mają adnotacje wiążące nazwy.

Dokumentacja



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow