在REST服务中实现方法OPTIONS的最佳方法

问题描述 投票:2回答:6

我正在做一个REST应用程序。我没有问题地制作了GET方法,但是,当我实现POST方法时,它说我没有为它实现OPTIONS方法。我正在为URI执行OPTIONS方法:

http://192.168.1.26:8080/sellAppWeb/api/object/

我有POSTOPTIONS方法:

@OPTIONS
@Produces("application/json; charset=UTF-8")
public Response options() {
    return Response.ok().build();
}

@Override
@POST
public Response save(CervejaDTO cervejaDTO) {
    cervejaController.register(cervejaDTO);
    return Response.ok(cervejaDTO).build();
}

然后我做了DELETE方法再次它说我没有OPTIONS方法。然后我需要制作另一个OPTIONS方法,它在URI端有一个ID。例如,使用id = 3删除对象:

http://192.168.1.26:8080/sellAppWeb/api/object/3

我需要另一个OPTIONS具有相同的DELETE URI结构:

@OPTIONS
@Path("/{id}")
@Produces("application/json; charset=UTF-8")
public Response optionsDelete(@PathParam("id") Integer id) {
    return Response.ok().build();
}

@Override
@POST
public Response save(CervejaDTO cervejaDTO) {
    cervejaController.register(cervejaDTO);
    return Response.ok(cervejaDTO).build();
}

有没有人有办法为所有REST请求做一个通用的OPTIONS

web.xml:

<display-name>Testes de serviços REST</display-name>
<description>Testes de serviços REST</description>

<welcome-file-list>
    <welcome-file>index.html</welcome-file>
</welcome-file-list>

<context-param>
    <param-name>resteasy.scan</param-name>
    <param-value>true</param-value>
</context-param>

<context-param>
    <param-name>resteasy.servlet.mapping.prefix</param-name>
    <param-value>/api</param-value>
</context-param>

<context-param>
    <param-name>resteasy.providers</param-name>
    <param-value>br.com.sell.app.exception.handler.DefaultExceptionHandler</param-value>
</context-param>

<listener>
    <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>

<servlet>
    <servlet-name>resteasy-servlet</servlet-name>
    <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>resteasy-servlet</servlet-name>
    <url-pattern>/api/*</url-pattern>
</servlet-mapping>

java rest java-ee jax-rs resteasy
6个回答
3
投票

在这种情况下,您不需要实现OPTIONS HTTP VERB。由于您正在使用RESTEasy,这是Wildfly使用的JAX-RS实现,我遇到的问题是由于web.xml上的servlet映射。

当我在Eclipse上添加JAX-RS facet并告诉它更新web.xml时,我遇到了这个问题。包含Restful应用程序映射的默认生成的web.xml不会将您的应用程序正确映射到RESTful资源路径。

这就是web.xml的样子,只要你没有创建自己的自定义Application

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    version="3.1">
    <display-name>My REST API</display-name>
    <description>My REST API</description>
    <servlet>
        <description>JAX-RS Tools Generated - Do not modify</description>
        <servlet-name>javax.ws.rs.core.Application</servlet-name>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>javax.ws.rs.core.Application</servlet-name>
        <url-pattern>/jaxrs/*</url-pattern>
    </servlet-mapping>
</web-app>

确保您的<servlet-name><servlet-mapping>如上例所示进行映射。如果你扩展了Application类,只需在web.xml中指定它,而不是如上所示的默认Application

此外,您的@POST资源方法,建议使用@Consumes注释指定RESTful数据的资源类型(在您的情况下,您的DTO)。

例如。

@POST
@Path("/save")
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response save(CervejaDTO cervejaDTO)

}

3
投票

“但是,当我实现POST方法时,它表示我没有为它实现OPTIONS方法。”

“当我发出POST或DELETE请求时,应用程序会在”之前自动生成OPTIONS请求“

这肯定听起来像CORS(跨源资源共享)问题。您可以在HTTP access control (CORS)上阅读更多相关信息。基本上OPTIONS请求是在实际请求之前的预检请求。对于某些类型的AJAX请求,会发生这种情况。

为此,RESTeasy有你可以注册的CorsFilter。您需要将过滤器配置为您要允许的设置。另请参阅here示例以了解配置它的一种方法。


2
投票

我尝试了RestEasy's CorsFilter,但是使用OPTIONS方法进行的调用正在返回

RESTEASY003655:找不到选项的资源方法,使用Allow标头返回OK

我写了一个简单的过滤器:

  1. 确保您需要的CORS标头应用于响应。
  2. 使用OPTIONS方法调用端点时返回HTTP状态代码200。您只需告诉客户其CORS预检请求已被接受。

这是代码。这是一个简化版本,ditry - 但效率很高。如果您只想在查询“真实”端点时发回200,请随意优化过滤器。

@Provider 
public class CorsFilter implements ContainerResponseFilter {  

  @Override
  public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
    MultivaluedMap<String, Object> headers = responseContext.getHeaders();
    headers.add("Access-Control-Allow-Origin", "*"); // If you want to be more restrictive it could be localhost:4200
    headers.add("Access-Control-Allow-Methods", "GET, PUT, POST, OPTIONS"); // You can add HEAD, DELETE, TRACE, PATCH
    headers.add("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept, Accept-Language"); // etc

    if (requestContext.getMethod().equals("OPTIONS"))
        responseContext.setStatus(200);
}}

来自this post和我的preferred CORS explanation


1
投票

我建议您使用Spring Controllers和RequestMapping注释,它们非常易于使用:

@RequestMapping(value="/method0", method="POST")
@ResponseBody
public String method0(){
    return "method0";
}

您不需要实现OPTIONS方法,只需声明您的方法并使用注释将其定义为POST / GET / PUT / DELETE请求方法。 Here有很多例子。


1
投票

对我来说,这是唯一的工作方式。

在java restclient项目中创建类。

    import javax.ws.rs.core.Context;
    import javax.ws.rs.core.HttpHeaders;
    import javax.ws.rs.core.Response;
    import javax.ws.rs.core.Response.ResponseBuilder;
    import javax.ws.rs.ext.ExceptionMapper;
    import javax.ws.rs.ext.Provider;

    import org.jboss.resteasy.spi.DefaultOptionsMethodException;

    @Provider
    public class OptionsHander implements
         ExceptionMapper<DefaultOptionsMethodException> {

    private static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
    private static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";

    private static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
    private static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
    private static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";

    private static final String ACCESS_CONTROL_ALLOW_ORIGIN_ANYONE = "*";

    @Context HttpHeaders httpHeaders;

    @Override
    public Response toResponse(DefaultOptionsMethodException exception) {

        final ResponseBuilder response = Response.ok();

        String requestHeaders = httpHeaders.getHeaderString(ACCESS_CONTROL_REQUEST_HEADERS);
        String requestMethods = httpHeaders.getHeaderString(ACCESS_CONTROL_REQUEST_METHOD);

        if (requestHeaders != null)
            response.header(ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders);

        if (requestMethods != null)
            response.header(ACCESS_CONTROL_ALLOW_METHODS, requestMethods);

        // TODO: development only, too permissive
        response.header(ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_ALLOW_ORIGIN_ANYONE);

        return response.build();
    }
}

0
投票

您可以使用@Path(“{path:。*}”)。

@OPTIONS
@Path("{path:.*}")
public Response handleCORSRequest() throws Exception {
    Response.ResponseBuilder builder = Response.ok();
    return builder.build();
}
© www.soinside.com 2019 - 2024. All rights reserved.