Java API for RESTful Webservices (JAX-RS) ist ein Standard, mit dem man REST-APIs in Java implementieren kann. Dabei werden Klassen und Methoden mit Annotationen markiert, damit diese dann auf HTTP-Requests reagieren und passende Responses zu schicken.

JAX-RS bedarf einer Library, die es implementiert (Annotationen auswerten, Serverinstanz starten, Anfragen an Methoden weiterleiten…), z.B. Jersey.

Beispiel

@Path("students")
public class StudentService {
 
	@GET
	@Path("{id}")
	public Student read(@PathParam("id") int mnr) {
		Student s;
		// s aus Datenbank lesen
		return s;
	}
 
}

Annotationen

Für Klassen

  • @XMLRootElement - Markiert die Klasse als Wurzelelement für XML-Serialisierung. Wurzelelemente sollten eigenständige Entitäten sein.
  • @XmlAccessorType(type) - Legt fest, wie auf Methoden/Felder zugegriffen wird
    • XmlAccessorType.Field - Direkter Zugriff auf Felder (auch private)
    • XmlAccessorType.PROPERTY - Zugriff über Getter und Setter
    • XmlAccessorType.PUBLIC_MEMBER - Öffentliche Felder
    • XmlAccessorType.NONE - Nur explizit annotierte Felder und Methoden

Für Attribute

  • @XmlAttribute - Markiert ein Feld als XML-Attribut statt als Element
    Beispiel: <student studentNumber="123">/*...*/</student>
    statt <student><studentNumber>123</studentNumber>/*...*/</student>
  • @XmlTransiet - Schließt ein Feld/Methode von der XML-Serialisierung aus

Für Methoden

  • @Path(path) - Definiert den Pfad der URL zur Funktion (kann man auch an Klassen schreiben)
  • @POST, @GET, @PUT, @DELETE - Definiert mit welchen Methoden die URL aufzurufen ist
  • @Produces(type) - Definiert welcher Content-Type als Response gesendet werden darf
    • MediaType.APPLICATION_XML - XML

JSON

  • @Consumes(MediaType.APPLICATION_JSON)
  • @Produces(MediaType.APPLICATION_JSON)
  • @Singleton

Beispiel

@Singleton // Ohne diese Annotation würde für jede Request ein neuer Service erstellt werden -> Datenverlust
@Path("/todos")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class ToDoService {
 
    private final Map<Integer, Todo> todos = new ConcurrentHashMap<>();
    private final AtomicInteger id = new AtomicInteger(1);
 
    @GET
    public Collection<Todo> getToDos() {
        return todos.values();
    }
 
    @POST
    public Todo createToDo(Todo todo) {
        int newId = id.getAndIncrement();
        todo.setId(newId);
        todos.put(newId, todo);
        return todo;
    }
 
    @GET
    @Path("/{id}")
    public Todo readToDo(@PathParam("id") int id) {
        Todo todo = todos.get(id);
 
        if(todo == null) {
	        throw new NotFoundException();
        }
 
        return todo;
    }
}

XML

Für XML bräuchte der Service die Annotationen

@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)

und das Model bräuchte zusätzlich

 
@XmlRootElement(name = "todo")
@XmlAccessorType(XmlAccessType.FIELD)

Woran liegt das?

Das hängt mit den unterschiedlichen Mappern zusammen, die für XML bzw. JSON verwendet werden. Der JSON-Mapper, meist Jackson, nutzt Reflection und erkennt die Objekte automatisch. Der XML-Mapper JAXB erwartet dass der Programmierer die Elemente selbst definiert.