拥抱Restful--Jersey框架

2,956 阅读3分钟

引言

提到 RestFul ,很容易联想到 Spring 框架,如果你在此之前没有接触过 Spring 框架,建议先点击 传送门 了解一下如何通过 Spring 快速构建一个 Restful Web 服务。

今天我们主要通过另一个轻量级的框架-- Jersey 整合 Spring 来构建一个 Restful 服务。 Jersey 是一个用于构建 Restful Web 服务的一个开源框架。是 JAX-RS 规范的一种具体实现。

Maven 依赖

首先引入 Jersey 相关的依赖:

    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet</artifactId>
        <version>3.0.1</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>3.0.1</version>
    </dependency>

如果要整合 Spring 框架的话,我们还需要引入 jersey-spring4 依赖:

    <!-- 整合spring -->
    <dependency>
        <groupId>org.glassfish.jersey.ext</groupId>
        <artifactId>jersey-spring4</artifactId>
        <version>3.0.0-M6</version>
    </dependency>

Web 配置

接下来,我们需要建立一个 Web 项目来进行 Servlet 配置,使用 Spring 的 WebApplicationInitializer 。

@Order(Ordered.HIGHEST_PRECEDENCE)
public class ApplicationInitializer implements WebApplicationInitializer {

    public void onStartup(javax.servlet.ServletContext servletContext) {
        AnnotationConfigWebApplicationContext context
                = new AnnotationConfigWebApplicationContext();

        servletContext.addListener(new ContextLoaderListener(context));
        servletContext.setInitParameter("contextConfigLocation", "com.example.server");
    }
}

这里添加 @Order(Ordered.HIGHEST_PRECEDENCE) 注解的作用是确保 WebApplicationInitializer 在 Jersey-Spring 默认初始化程序之前执行。

使用 Jersey JAX-RS 服务

资源表示类

@XmlRootElement
public class Employee {
    private int id;
    private String firstName;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}

当仅需要 XML 支持(除了JSON)时,才需要引入 @XmlRootElement 这一类的 JAXB 注解,

实现服务

现在我们通过 JAX-RS 注解来构建一个 RESTful Web 服务:

@Path("/employees")
public class EmployeeResource {
 
    @Autowired
    private EmployeeRepository employeeRepository;

    @GET
    @Path("/{id}")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Employee getEmployee(@PathParam("id") int id) {
        return employeeRepository.getEmployee(id);
    }

    @POST
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response addEmployee(
      Employee employee, @Context UriInfo uriInfo) {
 
        employeeRepository.addEmployee(new Employee(employee.getId(), 
          employee.getFirstName(), employee.getLastName(), 
          employee.getAge()));
 
        return Response.status(Response.Status.CREATED.getStatusCode())
          .header(
            "Location", 
            String.format("%s/%s",uriInfo.getAbsolutePath().toString(), 
            employee.getId())).build();
    }
}

@Path 注解表示服务请求路由,我们也可以将变量嵌入到请求路由中,如代码中的 {id} 。如果需要获取该变量值,可以通过 @PathParam 注解。

@GET@PUT@POST , @DELETE@HEAD 标识请求 HTTP 的方式。

@Produces 表示响应的数据类型,在示例中,我们已将其配置为根据HTTP请求头为 (Acceptapplication/json 或 application/xml) 的值返回JSON或XML。

@Consumes 定义服务可用的 MIME 类型,具体的数据类型取决于请求头 Content-Type(application/json或application/xml)。

@Context 注解用于将信息注入类字段,bean属性或方法参数。在示例中,我们使用它来注入 UriInfo 。我们还可以使用它来注入 ServletConfig , ServletContext , HttpServletRequest 和 HttpServletResponse 。

使用 ExceptionMapper

ExceptionMapper 可以帮助我们拦截异常并将适当的HTTP响应代码返回给客户端。在以下示例中,如果抛出 EmployeeNotFound 异常,则返回 HTTP 响应代码 404 :

@Provider
public class NotFoundExceptionHandler 
 implements ExceptionMapper<EmployeeNotFound> {

   public Response toResponse(EmployeeNotFound ex) {
       return Response.status(Response.Status.NOT_FOUND).build();
   }
}

管理资源类

最后,让我们根据应用程序路径连接所有服务实现类和异常映射:

@ApplicationPath("/resources")
public class RestConfig extends Application {
    public Set<Class<?>> getClasses() {
        return new HashSet<Class<?>>(
          Arrays.asList(
            EmployeeResource.class, 
            NotFoundExceptionHandler.class, 
            AlreadyExistsExceptionHandler.class));
    }
}

API 测试

public class JerseyApiLiveTest {

    private static final String SERVICE_URL
      = "http://localhost:8082/spring-jersey/resources/employees";

    @Test
    public void givenGetAllEmployees_whenCorrectRequest_thenResponseCodeSuccess() 
      throws ClientProtocolException, IOException {
 
        HttpUriRequest request = new HttpGet(SERVICE_URL);

        HttpResponse httpResponse = HttpClientBuilder
          .create()
          .build()
          .execute(request);

        assertEquals(httpResponse
          .getStatusLine()
          .getStatusCode(), HttpStatus.SC_OK);
    }
}

总结

本文介绍了 Jersey 框架并开发了一个简单的 API 。我们引入 Spring 用于依赖关系注入功能。还看到了 ExceptionMapper 的使用。