簡體   English   中英

通過JAXB將鏈接插入RESTEasy XML結果

[英]Inserting links into RESTEasy XML results via JAXB

我想通過RESTeasy / JAXB將鏈接插入XML。 我試圖使用我的代碼的文檔 ,但這不起作用,所以我只是在文檔中編寫給定的示例:它仍然不起作用,我不知道為什么。

背景:

要在我的JBoss RESTEasy API中實現HATEOAS原則,我必須在我的JAXB XML結果中插入鏈接,以便客戶端可以瀏覽API。

我現在正試圖了解如何做到這一點,但我不確定文檔是否充滿錯誤,或者我只是無法理解示例和解釋:

不清楚的東西

據我了解,你必須使用@AddLinks來聲明結果應該插入鏈接。 然后我必須使用@LinkResource 再次 (!?) 冗余地執行此操作,並且“ 有時 ”指定URI構建過程應該來自哪個類(例如@LinkResource(value = car.class) )。 然后我必須在實體類中添加一個RESTServiceDiscovery ,用@XmlElementRef注釋它......但在示例中,在聲明(!?)之后, RESTServiceDiscovery根本沒有被使用。

碼:

我真的很困惑如何使用所有這些,但當然我自己嘗試了很多代碼,讓它工作。
以下代碼就像文檔示例:

BookController.java

import java.util.ArrayList;
import java.util.Collection;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import org.jboss.resteasy.links.AddLinks;
import org.jboss.resteasy.links.LinkResource;

import com.gasx.extsys.datamodel.vo.kplan.Book;

@Path("/")
@Consumes({ "application/xml", "application/json" })
@Produces({ "application/xml", "application/json" })
public class BookController {
    @AddLinks
    @LinkResource(value = Book.class)
    @GET
    @Path("books")
    public Collection<Book> getBooks() {
        ArrayList<Book> res = new ArrayList<Book>();
        res.add(new Book("Robert", "WhySOIsGreat"));
        res.add(new Book("Robert", "JavaUltimateGuide"));
        res.add(new Book("Not Robert", "ThisIsSparta!"));
        return res;
    };

    @AddLinks
    @LinkResource
    @GET
    @Path("book/{id}")
    public Book getBook(@PathParam("id") String id) {
        return new Book("Robert", "WhyIloveJAVA");
    };
} 

Book.java

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlRootElement;

import org.jboss.resteasy.links.RESTServiceDiscovery;

@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class Book {
    @XmlAttribute
    private String author = "startAuthor";

    @XmlID
    @XmlAttribute
    private String title = "startTitle";

    @XmlElementRef
    private RESTServiceDiscovery rest;

    public Book() {

    }

    public Book(String author, String title) {
        this.author = author;
        this.title = title;
    }
}

現在在booksbook/1上調用GET會拋出此錯誤:

2014-09-25 11:30:36,188 WARN  [http-/0.0.0.0:8080-1] (org.jboss.resteasy.core.SynchronousDispatcher:135) # Failed executing GET /book/1: org.jboss.resteasy.plugins.providers.jaxb.JAXBMarshalException: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
XmlElementRef points to a non-existent class.

我不知道這到底是怎么工作的,所以我試着用以下的Book.java代碼手動添加的URI:

import java.net.URI;
    public Book(String author, String title) {
        this.author = author;
        this.title = title;
        URI uri = URI.create("books/" + title);
        rest = new RESTServiceDiscovery();
        rest.addLink(uri, "self");
    }

但這仍然會引發同樣的錯誤。

我不太熟悉鏈接注入,但添加鏈接的一種簡單方法是將javax.ws.rs.core.Link嵌入到JAXB實體類中。 它帶有一個內置的XmlAdapter, Link.JaxbAdapter ,它允許Link類型由JAXB編組Link.JaxbAdapter 例如,您有一個BookStore類,其中包含一系列Books 它還將具有Link ,您可以從中控制導航案例。

@XmlRootElement(name = "bookstore")
public class BookStore {

    private List<Link> links;
    private Collection<Book> books;

    @XmlElementRef
    public Collection<Book> getBooks() {
        return books;
    }

    public void setBooks(Collection<Book> books) {
        this.books = books;
    }

    @XmlElement(name = "link")
    @XmlJavaTypeAdapter(Link.JaxbAdapter.class) 
    public List<Link> getLinks() {
        return links;
    }

    public void setLinks(List<Link> links) {
        this.links = links;
    }

    @XmlTransient
    public URI getNext() {
        if (links == null) {
            return null;
        }
        for (Link link : links) {
            if ("next".equals(link.getRel())) {
                return link.getUri();
            }
        }
        return null;
    }

    @XmlTransient
    public URI getPrevious() {
        if (links == null) {
            return null;
        }
        for (Link link : links) {
            if ("previous".equals(link.getRel())) {
                return link.getUri();
            }
        }
        return null;
    }
}

Book類只是一個常規的根元素JAXB類

@XmlRootElement
public class Book {

    @XmlAttribute
    private String author;

    @XmlAttribute
    private String title;

    public Book() {}

    public Book(String title, String author) {
        this.title = title;
        this.author = author;
    }
}

BookResource類中,我們基本上可以根據您想要表示的鏈接所需的邏輯按需添加鏈接。 在下面的示例中,有一個內存數據庫(此類僅用作有狀態單例類)的書籍,我為其添加了五本書,並增加了ID。 當請求進入時,將向返回的BookStore添加一個或兩個鏈接。 根據請求的ID,我們將添加“下一個”和/或之前的“鏈接。鏈接將具有我們從BookStore類引用的rel

@Path("/books")
public class BookResource {

    private final Map<Integer, Book> booksDB 
            = Collections.synchronizedMap(new LinkedHashMap<Integer, Book>());
    private final AtomicInteger idCounter = new AtomicInteger();

    public BookResource() {
        Book book = new Book("Book One", "Author One");
        booksDB.put(idCounter.incrementAndGet(), book);

        book = new Book("Book Two", "Author Two");
        booksDB.put(idCounter.incrementAndGet(), book);

        book = new Book("Book Three", "Author Three");
        booksDB.put(idCounter.incrementAndGet(), book);

        book = new Book("Book Four", "Author Four");
        booksDB.put(idCounter.incrementAndGet(), book);

        book = new Book("Book Five", "Author Five");
        booksDB.put(idCounter.incrementAndGet(), book);
    }

    @GET
    @Formatted
    @Path("/{id}")
    @Produces(MediaType.APPLICATION_XML)
    public BookStore getBook(@Context UriInfo uriInfo, @PathParam("id") int id) {
        List<Link> links = new ArrayList<>();
        Collection<Book> books = new ArrayList<>();

        UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
        uriBuilder.path("books");
        uriBuilder.path("{id}");

        Book book = booksDB.get(id);
        if (book == null) {
            throw new WebApplicationException(Response.Status.NOT_FOUND);
        }

        synchronized(booksDB) {
            if (id + 1 <= booksDB.size()) {
                int next = id + 1;
                URI nextUri = uriBuilder.clone().build(next);
                Link link = Link.fromUri(nextUri).rel("next").type(MediaType.APPLICATION_XML).build();
                links.add(link);
            }
            if (id - 1 > 0) {
                int previous = id - 1;
                URI nextUri = uriBuilder.clone().build(previous);
                Link link = Link.fromUri(nextUri).rel("previous").type(MediaType.APPLICATION_XML).build();
                links.add(link);
            }
        }

        books.add(book);
        BookStore bookStore = new BookStore();
        bookStore.setLinks(links);
        bookStore.setBooks(books);
        return bookStore;
    }
}

在測試用例中,我們請求第三本書,我們可以看到內存數據庫中有“下一頁”和“上一頁”書籍的鏈接。 我們還在BookStore上調用getNext()來檢索db中的下一本書,結果將帶有兩個不同的鏈接。

public class BookResourceTest {

    private static Client client;

    @BeforeClass
    public static void setUpClass() {
        client = ClientBuilder.newClient();
    }

    @AfterClass
    public static void tearDownClass() {
        client.close();
    }

    @Test
    public void testBookResourceLinks() throws Exception {
        String BASE_URL = "http://localhost:8080/jaxrs-stackoverflow-book/rest/books/3";
        WebTarget target = client.target(BASE_URL);
        String xmlResult = target.request().accept(MediaType.APPLICATION_XML).get(String.class);
        System.out.println(xmlResult);

        Unmarshaller unmarshaller = JAXBContext.newInstance(BookStore.class).createUnmarshaller();
        BookStore bookStore = (BookStore)unmarshaller.unmarshal(new StringReader(xmlResult));
        URI next = bookStore.getNext();

        WebTarget nextTarget = client.target(next);
        String xmlNextResult = nextTarget.request().accept(MediaType.APPLICATION_XML).get(String.class);
        System.out.println(xmlNextResult);
    }
}

結果:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bookstore>
    <book author="Author Three" title="Book Three"/>
    <link href="http://localhost:8080/jaxrs-stackoverflow-book/rest/books/4" rel="next" type="application/xml"/>
    <link href="http://localhost:8080/jaxrs-stackoverflow-book/rest/books/2" rel="previous" type="application/xml"/>
</bookstore>

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bookstore>
    <book author="Author Four" title="Book Four"/>
    <link href="http://localhost:8080/jaxrs-stackoverflow-book/rest/books/5" rel="next" type="application/xml"/>
    <link href="http://localhost:8080/jaxrs-stackoverflow-book/rest/books/3" rel="previous" type="application/xml"/>
</bookstore>

僅供參考,我正在使用Resteasy 3.0.8和Wildfly 8.1


更新:使用自動發現

所以我嘗試了參考指南示例,我無法重現您的問題。 不確定你的完整環境,但這是我正在使用的

  • Wildfly 8.1
  • Resteasy 3.0.8
  • Maven的

這是代碼

應用類

@ApplicationPath("/rest")
public class BookApplication extends Application {
    @Override
    public Set<Class<?>> getClasses() {
       Set<Class<?>> classes = new HashSet<>();
       classes.add(Bookstore.class);
       return classes;
   }
}

資源類

@Path("/books")
@Produces({"application/xml", "application/json"})
public class Bookstore {

    @AddLinks
    @LinkResource(value = Book.class)
    @GET
    @Formatted
    public Collection<Book> getBooks() {
        List<Book> books = new ArrayList<>();
        books.add(new Book("Book", "Author"));
        return books;
    }
}

預訂課程

@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class Book {
    @XmlAttribute
    private String author;
    @XmlID @XmlAttribute
    private String title; 
    @XmlElementRef
    private RESTServiceDiscovery rest;

    public Book() {}

    public Book(String title, String author) {
        this.title = title;
        this.author = author;
    }
}

pom.xml(也許你缺少一些依賴項 - 注意下面的resteasy-client和resteasy-servlet-initializer僅用於測試)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.underdogdevs.web</groupId>
    <artifactId>jaxrs-stackoverflow-user</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>jaxrs-stackoverflow-user</name>

    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jackson2-provider</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jaxb-provider</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jaxrs</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>jaxrs-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-links</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-client</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-servlet-initializer</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.wildfly.bom</groupId>
                <artifactId>jboss-javaee-7.0-with-resteasy</artifactId>
                <version>8.1.0.Final</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.wildfly.bom</groupId>
                <artifactId>jboss-javaee-7.0-with-tools</artifactId>
                <version>8.1.0.Final</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${endorsed.dir}</outputDirectory>
                            <silent>true</silent>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>javax</groupId>
                                    <artifactId>javaee-endorsed-api</artifactId>
                                    <version>7.0</version>
                                    <type>jar</type>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

在瀏覽器中正常工作

在此輸入圖像描述

適用於客戶端api

public class BookTest {
    private static Client client;
    @BeforeClass
    public static void setUpClass() {
        client = ClientBuilder.newClient();
    }
    @AfterClass
    public static void tearDownClass() {
        client.close();
    }

    @Test
    public void testBookLink() {
        String BASE_URL
                = "http://localhost:8080/jaxrs-stackoverflow-user/rest/books";
        WebTarget target = client.target(BASE_URL);
        String result = target.request()
                .accept(MediaType.APPLICATION_XML).get(String.class);
        System.out.println(result);
    }
}

結果

Running jaxrs.book.test.BookTest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<collection xmlns:atom="http://www.w3.org/2005/Atom">
    <book author="Author" title="Book">
        <atom:link rel="list" href="http://localhost:8080/jaxrs-stackoverflow-user/rest/books"/>
    </book>
</collection>

至於你的不清楚的東西

使用@AddLinks注釋JAX-RS方法,以指示您希望在響應實體中注入Atom鏈接。

這是為了表明該方法將使用鏈接注入。

使用@LinkResource注釋您希望Atom鏈接的JAX-RS方法,以便RESTEasy知道要為哪些資源創建哪些鏈接。

這允許您自定義注入的鏈接和實體。 8.2.4。 指定哪些J​​AX-RS方法與哪些資源的關聯更深入。

將RESTServiceDiscovery字段添加到要注入Atom鏈接的資源類中。

“注入”意味着框架將為您實例化,因此您永遠不必自己明確地執行此操作(正如您嘗試的那樣)。 也許對依賴注入和控制反轉(IoC)進行一些研究

祝好運。 希望這一切都有幫助。

我只想補充一點,如果你想在JSON中添加你的ATOM鏈接,你需要禁用/排除所有的jackson提供者。 即在WildFly 8.x中創建一個META-INF / jboss-deployment-structure.xml,其中包含以下內容:

<jboss-deployment-structure>
  <deployment>
    <exclusions>
      <module name="org.jboss.resteasy.resteasy-jackson-provider" />
      <module name="org.jboss.resteasy.resteasy-jackson2-provider" />
    </exclusions>
    <dependencies>
      <module name="org.jboss.resteasy.resteasy-jettison-provider" />
    </dependencies>
  </deployment>
</jboss-deployment-structure>

這樣,jettison提供程序將正確創建包含ATOM鏈接的JSON表示

希望這可以幫助 ;)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM