Рубрики
Без рубрики

Примеры Джерси и JSON (EclipseLink moxy)

Ответ JSON в Джерси (с использованием MOXy), пользовательская конфигурация Moxy, пользовательский сопоставитель исключений, модульное тестирование конечных точек ответа JSON в Джерси и т.д.

Автор оригинала: mkyong.

В этой статье показано, как вернуть JSON ответ в приложении Джерси , используя EclipseLink Moxy .

Протестировано с

  • Джерси 3.0.2
  • EclipseLink Мокси 3
  • Причал 11, HTTP-сервер
  • Java 11
  • Мавен 3
  • SLF4J, Возврат в систему, перенаправление журналов Jersey J.U.L для возврата в систему
  • JUnit 5 и JSONassert 1.5 (модульный тест)
  • org.json 20210307, jsonобъект

Содержание

  • 1. EclipseLink Moxy как поставщик JSON в Джерси
  • 2. Каталог проектов
  • 3. Зависимости проекта
  • 4. Верните ответ JSON в Джерси
  • 5. Запустите приложение Джерси
  • 6. ДЕМОНСТРАЦИЯ
  • 7. Настраиваемая конфигурация Moxy
  • 8. ExceptionMapper – Пользовательский ответ JSON 404 в Джерси
    • 8.1 Джерси 404 не найдено
    • 8.2 Исключение и исключение NotFoundException
    • 8.3 Зарегистрируйте пользовательский редактор исключений в ResourceConfig
    • 8.3 ДЕМОНСТРАЦИЯ с пользовательским приложением NotFoundExceptionMapper
    • 8.4 Исключение веб-приложения
  • 9. Модульное тестирование конечных точек JSON Джерси
  • 10. Скачать Исходный Код
  • 11. Рекомендации

1. EclipseLink Moxy как поставщик JSON в Джерси

Мы можем включить джерси-медиа-мокси , чтобы сделать EclipseLink Мокси поставщиком JSON в приложении Джерси.

  
  
      org.glassfish.jersey.media
      jersey-media-moxy
  

2. Каталог проектов

Просмотрите каталог стандартных проектов Maven.

3. Зависимости проекта

Просмотрите зависимости проекта.

  
      UTF-8
      11
      11
      11
      5.4.0
      1.5.0
      3.0.2
      1.2.3
      1.7.31
      20210307
      5.0.0
  

  
      
          
              org.glassfish.jersey
              jersey-bom
              ${jersey.version}
              pom
              import
          
      
  

  

      
      
          org.glassfish.jersey.containers
          jersey-container-jetty-http
      

      
      
      
          jakarta.servlet
          jakarta.servlet-api
          ${servlet.api.version}
      

      
      
          org.glassfish.jersey.inject
          jersey-hk2
      

      
      
          org.glassfish.jersey.media
          jersey-media-moxy
      

      
      
          org.json
          json
          ${org.json.version}
      

      
      
          org.slf4j
          jul-to-slf4j
          ${slf4j.version}
      

      
      
          ch.qos.logback
          logback-classic
          ${logback.version}
      

      
      
          org.junit.jupiter
          junit-jupiter-params
          ${junit.version}
          test
      

      
      
          org.skyscreamer
          jsonassert
          ${jsonassert.version}
          test
      

  
mvn dependency:tree

[INFO] Scanning for projects...
[INFO]
[INFO] -------------------< com.mkyong:jersey-json-example >-------------------
[INFO] Building jersey-json-example 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ jersey-json-example ---
[INFO] com.mkyong:jersey-json-example:jar:1.0
[INFO] +- org.glassfish.jersey.containers:jersey-container-jetty-http:jar:3.0.2:compile
[INFO] |  +- jakarta.inject:jakarta.inject-api:jar:2.0.0:compile
[INFO] |  +- org.eclipse.jetty:jetty-server:jar:11.0.0:compile
[INFO] |  |  +- org.eclipse.jetty:jetty-http:jar:11.0.0:compile
[INFO] |  |  \- org.eclipse.jetty:jetty-io:jar:11.0.0:compile
[INFO] |  +- org.eclipse.jetty:jetty-util:jar:11.0.0:compile
[INFO] |  +- org.glassfish.jersey.core:jersey-common:jar:3.0.2:compile
[INFO] |  |  +- jakarta.annotation:jakarta.annotation-api:jar:2.0.0:compile
[INFO] |  |  \- org.glassfish.hk2:osgi-resource-locator:jar:1.0.3:compile
[INFO] |  +- org.glassfish.jersey.core:jersey-server:jar:3.0.2:compile
[INFO] |  |  +- org.glassfish.jersey.core:jersey-client:jar:3.0.2:compile
[INFO] |  |  \- jakarta.validation:jakarta.validation-api:jar:3.0.0:compile
[INFO] |  \- jakarta.ws.rs:jakarta.ws.rs-api:jar:3.0.0:compile
[INFO] +- jakarta.servlet:jakarta.servlet-api:jar:5.0.0:compile
[INFO] +- org.glassfish.jersey.inject:jersey-hk2:jar:3.0.2:compile
[INFO] |  +- org.glassfish.hk2:hk2-locator:jar:3.0.1:compile
[INFO] |  |  +- org.glassfish.hk2.external:aopalliance-repackaged:jar:3.0.1:compile
[INFO] |  |  +- org.glassfish.hk2:hk2-api:jar:3.0.1:compile
[INFO] |  |  \- org.glassfish.hk2:hk2-utils:jar:3.0.1:compile
[INFO] |  \- org.javassist:javassist:jar:3.25.0-GA:compile
[INFO] +- org.glassfish.jersey.media:jersey-media-moxy:jar:3.0.2:compile
[INFO] |  +- org.glassfish.jersey.ext:jersey-entity-filtering:jar:3.0.2:compile
[INFO] |  +- jakarta.json:jakarta.json-api:jar:2.0.0:compile
[INFO] |  +- jakarta.json.bind:jakarta.json.bind-api:jar:2.0.0:compile
[INFO] |  +- org.glassfish:jakarta.json:jar:module:2.0.0:compile
[INFO] |  \- org.eclipse.persistence:org.eclipse.persistence.moxy:jar:3.0.0:compile
[INFO] |     +- jakarta.xml.bind:jakarta.xml.bind-api:jar:3.0.0:compile
[INFO] |     +- org.eclipse.persistence:org.eclipse.persistence.asm:jar:3.0.0:compile
[INFO] |     +- org.eclipse.persistence:org.eclipse.persistence.core:jar:3.0.0:compile
[INFO] |     +- com.sun.activation:jakarta.activation:jar:2.0.0:runtime
[INFO] |     \- com.sun.mail:jakarta.mail:jar:2.0.0:runtime
[INFO] +- org.json:json:jar:20210307:compile
[INFO] +- org.slf4j:jul-to-slf4j:jar:1.7.31:compile
[INFO] |  \- org.slf4j:slf4j-api:jar:1.7.31:compile
[INFO] +- ch.qos.logback:logback-classic:jar:1.2.3:compile
[INFO] |  \- ch.qos.logback:logback-core:jar:1.2.3:compile
[INFO] +- org.junit.jupiter:junit-jupiter-params:jar:5.4.0:test
[INFO] |  +- org.apiguardian:apiguardian-api:jar:1.0.0:test
[INFO] |  \- org.junit.jupiter:junit-jupiter-api:jar:5.4.0:test
[INFO] |     +- org.opentest4j:opentest4j:jar:1.1.1:test
[INFO] |     \- org.junit.platform:junit-platform-commons:jar:1.4.0:test
[INFO] \- org.skyscreamer:jsonassert:jar:1.5.0:test
[INFO]    \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  7.664 s
[INFO] Finished at: 2021-06-23T16:00:25+08:00
[INFO] ------------------------------------------------------------------------

4. Верните ответ JSON в Джерси

Несколько конечных точек ответа JSON в приложении Jersey, и MOXy обработает преобразование объекта в/из JSON.

package com.mkyong.json;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.json.JSONObject;

import java.util.Arrays;
import java.util.List;

@Path("/json")
public class JsonResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response hello() {

        // create a JSON string
        JSONObject json = new JSONObject();
        json.put("result", "Jersey JSON example using MOXy");

        // MUST json.toString(), else empty result or Unconsumed content
        // return Response.status(Response.Status.OK).entity(json).build();
        return Response.status(Response.Status.OK).entity(json.toString()).build();

    }

    // Object to JSON
    @Path("/{name}")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public User hello(@PathParam("name") String name) {
        return new User(1, name);
    }

    // A list of objects to JSON
    @Path("/all")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List helloList() {
        return Arrays.asList(
                new User(1, "mkyong"),
                new User(2, "zilap")
        );
    }

    // POST, accepts JSON
    @Path("/create")
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response create(User user) {

        JSONObject json = new JSONObject();
        json.put("status", "ok");
        return Response.status(Response.Status.CREATED).entity(json.toString()).build();

    }

}

Ниже приведен объект User .

package com.mkyong.json;

// optional, for json response, no need @XmlRootElement
// only needed if works with XML
// @XmlRootElement
public class User {

    private int id;
    String name;

    // getters, setters and constructors and etc
}

5. Запустите приложение Джерси

Запускает HTTP-сервер Jetty и развертывает приложение Jersey по адресу http://localhost:8080 .

P.S Статическая инициализация необязательна, и она предназначена для настройки ведения журнала для перенаправления журналов J.U.L Джерси в реализацию обратного входа SLF4J.

package com.mkyong;

import com.mkyong.json.JsonResource;
import org.eclipse.jetty.server.Server;
import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import org.slf4j.bridge.SLF4JBridgeHandler;

import java.io.IOException;
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;

public class MainApp {

    public static final URI BASE_URI = URI.create("http://localhost:8080");

    // For logging, setup to redirect J.U.L to logback
    static {
        // JUL Root logger to the lowest level, so that bridge can intercept all j.u.l. logs
        Logger.getLogger("").setLevel(Level.FINEST);

        // Optionally remove existing handlers attached to j.u.l root logger
        // (since SLF4J 1.6.5)
        SLF4JBridgeHandler.removeHandlersForRootLogger();

        // add SLF4JBridgeHandler to j.u.l's root logger, should be done once during
        // the initialization phase of your application
        SLF4JBridgeHandler.install();
    }

    // Starts Jetty HTTP server
    public static Server startHttpServer() {

        final ResourceConfig config = new ResourceConfig();

        // scan only one resource
        config.register(JsonResource.class);

        return JettyHttpContainerFactory.createServer(BASE_URI, config);

    }

    public static void main(String[] args) throws Exception {

        try {

            final Server server = startHttpServer();

            server.start();

            // shut down hook
            Runtime.getRuntime().addShutdownHook(
                    new Thread(() -> {
                        try {
                            server.stop();
                        } catch (Exception e) {
                            Logger.getLogger(MainApp.class.getName()).log(Level.SEVERE, null, e);
                        }
                    })
            );

            System.out.println(String.format("Application started.%nStop the application using CTRL+C"));

            // block and wait shut down signal, like CTRL+C
            Thread.currentThread().join();

        } catch (InterruptedException | IOException ex) {
            Logger.getLogger(MainApp.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

}

6. ДЕМОНСТРАЦИЯ

Запускает Основное приложение и использует инструмент cURL для простого тестирования.

$ curl http://localhost:8080/json
{"result":"Jersey JSON example using MOXy"}

$ curl http://localhost:8080/json/jersey
{"id":1,"name":"jersey"}

$ curl http://localhost:8080/json/all
[{"id":1,"name":"mkyong"},{"id":2,"name":"zilap"}]

$ curl -v -H "Content-Type: application/json" -X POST -d "{\"id\" : 1,\"name\" : \"mkyong\"}" http://localhost:8080/json/create

*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> POST /json/create HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.55.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 28
>
* upload completely sent off: 28 out of 28 bytes
< HTTP/1.1 201 Created
< Date: Wed, 23 Jun 2021 06:48:53 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Server: Jetty(11.0.0)
<
{"status":"ok"}*

7. Настраиваемая конфигурация Moxy

7.1 В приведенной выше демонстрации ответ JSON находится в компактном режиме, поведение MOXY по умолчанию; Мы можем создать новый MoxyJsonConfig для настройки MOXY, например, включить JSON-ответ MOXY в красивом формате печати.

package com.mkyong.json;

import jakarta.ws.rs.ext.ContextResolver;
import jakarta.ws.rs.ext.Provider;
import org.glassfish.jersey.moxy.json.MoxyJsonConfig;

@Provider
public class CustomMoxyConfig implements ContextResolver {

    final MoxyJsonConfig moxyJsonConfig;

    public CustomMoxyConfig() {
        moxyJsonConfig = new MoxyJsonConfig();

        // pretty print
        moxyJsonConfig.setFormattedOutput(true);
    }

    @Override
    public MoxyJsonConfig getContext(Class type) {
        return moxyJsonConfig;
    }

}

7.2 Зарегистрируйте пользовательский MoxyJsonConfig в Настройка ресурсов .

  // Starts Jetty HTTP server
  public static Server startHttpServer() {

      final ResourceConfig config = new ResourceConfig();

      // scan only one resource
      config.register(JsonResource.class);

      // only register if contains custom moxy config
      config.register(CustomMoxyConfig.class);

      return JettyHttpContainerFactory.createServer(BASE_URI, config);

  }

7.3 Снова перезапустите Основное приложение , и теперь ответ JSON будет в красивом печатном формате.

$ curl http://localhost:8080/json/all
[ {
   "id" : 1,
   "name" : "mkyong"
}, {
   "id" : 2,
   "name" : "zilap"
} ]

8. ExceptionMapper – Пользовательский ответ JSON 404 в Джерси

Позже мы создадим пользовательский ExceptionMapper для перехвата исключения ошибки 404 по умолчанию и возврата нашего пользовательского ответа JSON.

8.1 Джерси 404 не найдено

Попробуйте получить доступ к какому-либо несуществующему URL-адресу, и Jersey вернет статус 404 и пустой ответ.

$ curl http://localhost:8080/json/all/error  

Мы можем использовать curl - v для отображения сведений о запросе и ответе, например, кода состояния ответа.

$ curl -v http://localhost:8080/json/all/error

*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /json/all/error HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Date: Wed, 23 Jun 2021 07:18:01 GMT
< Content-Length: 0
< Server: Jetty(11.0.0)
<

8.2 Исключение и исключение NotFoundException

Создайте пользовательский сопоставитель исключений NotFoundException для перехвата ошибки 404.

package com.mkyong.json;

import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import org.json.JSONObject;

@Provider
public class CustomNotFoundExceptionMapper
        implements ExceptionMapper {

    @Override
    public Response toResponse(NotFoundException exception) {

        JSONObject json = new JSONObject();
        json.put("error", "url not found!");
        return Response.status(Response.Status.NOT_FOUND)
                .entity(json.toString())
                .build();
    }

}

8.3 Зарегистрируйте пользовательский редактор исключений в ResourceConfig

  // Starts Jetty HTTP server
  public static Server startHttpServer() {

      final ResourceConfig config = new ResourceConfig();

      // scan only one resource
      config.register(JsonResource.class);

      // only register if contains custom moxy config
      config.register(CustomMoxyConfig.class);

      // only register if contains custom exception mapper
      config.register(CustomNotFoundExceptionMapper.class);

      return JettyHttpContainerFactory.createServer(BASE_URI, config);

  }

8.3 ДЕМОНСТРАЦИЯ с пользовательским приложением NotFoundExceptionMapper

Перезапустите приложение Jersey и снова получите доступ к несуществующему URL-адресу. Нью-Джерси вернет наш пользовательский ответ в формате JSON.

$ curl http://localhost:8080/json/all/error
{"error":"url not found!"}

$ curl -v http://localhost:8080/json/all/error
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /json/all/error HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Date: Wed, 23 Jun 2021 07:38:23 GMT
< Transfer-Encoding: chunked
< Server: Jetty(11.0.0)
<
{"error":"url not found!"}

8.4 Исключение веб-приложения

Предположим, мы не уверены в точном типе Исключения для перехвата, и мы пытаемся перехватить исключение JAX-RS WebApplicationException . В этом случае он должен соответствовать всем ошибкам, вызванным приложением Джерси.

По ИДЕЕ, мы можем поместить точку останова внутри в ответ и наблюдать за тем, как приложение Джерси выдает исключения.

package com.mkyong.json;

import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import org.json.JSONObject;

@Provider
public class CustomExceptionMapper
      implements ExceptionMapper {

  @Override
  public Response toResponse(WebApplicationException exception) {

      JSONObject json = new JSONObject();
      json.put("error", exception.getMessage());
      //json.put("error", "some error");

      // 400
      return Response.status(Response.Status.BAD_REQUEST)
              .entity(json.toString())
              .build();
  }

}

Все еще не удается поймать исключение? попробуйте реализовать ExceptionMapper<Исключение> .

9. Модульное тестирование конечных точек JSON Джерси

Ниже приведены некоторые тесты для вышеуказанных конечных точек Джерси.

package com.mkyong;

import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.eclipse.jetty.server.Server;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONAssert;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class JsonResourceTest {

  private static Server server;
  private static WebTarget target;

  @BeforeAll
  public static void beforeAllTests() {
      server = MainApp.startHttpServer();
      Client c = ClientBuilder.newClient();
      target = c.target(MainApp.BASE_URI.toString());
  }

  @AfterAll
  public static void afterAllTests() throws Exception {
      server.stop();
  }

  @Test
  public void testJson() throws JSONException {

      String actual = target.path("json").request().get(String.class);
      String expected = "{\"result\":\"Jersey JSON example using MOXy\"}";

      JSONAssert.assertEquals(expected, actual, false);

  }

  @Test
  public void testJsonName() throws JSONException {

      String response = target.path("json/mkyong")
              .request(MediaType.APPLICATION_JSON)
              .get(String.class);

      // convert json string to JSONObject
      JSONObject actual = new JSONObject(response);

      String expected = "{\"id\":1,\"name\":\"mkyong\"}";
      JSONAssert.assertEquals(expected, actual, false);

  }

  @Test
  public void testJsonAll() throws JSONException {

      String response = target.path("json/all")
              .request(MediaType.APPLICATION_JSON)
              .get(String.class);

      // convert json string to JSONArray
      JSONArray actual = new JSONArray(response);

      String expected = "[{\"id\":1,\"name\":\"mkyong\"},{\"id\":2,\"name\":\"zilap\"}]";
      JSONAssert.assertEquals(expected, actual, false);

  }

  @Test
  public void testJsonCreateOk() throws JSONException {

      String json = "{\"id\":1,\"name\":\"mkyong\"}";

      Response response = target.path("json/create")
              .request(MediaType.APPLICATION_JSON)
              .post(Entity.entity(json, MediaType.valueOf("application/json")));

      assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());

      // read response body
      String actual = response.readEntity(String.class);
      String expected = "{\"status\":\"ok\"}";
      JSONAssert.assertEquals(expected, actual, false);

  }

  @Test
  public void testJsonCustomNotFound() throws JSONException {

      Response response = target.path("json/all/error")
              .request(MediaType.APPLICATION_JSON).buildGet().invoke();

      // ensure 404 status code
      assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus());

      // test json response
      String actual = response.readEntity(String.class);
      String expected = "{\"error\":\"url not found!\"}";
      JSONAssert.assertEquals(expected, actual, false);

  }

}

Альтернатива, попробуйте Джексона |/Примеры Джерси и JSON (Джексон)

10. Скачать Исходный Код

$клон git https://github.com/mkyong/jax-rs

$cd jax-rs/джерси/джерси-json-мокси/

$ пакет mvn

$java -банка target/jersey-json-moxy.jar

11. Рекомендации

Оригинал: “https://mkyong.com/webservices/jax-rs/jersey-and-json-examples-eclipselink-moxy/”