【问题标题】:URI Recognition in Java Using JAX-RS @Path Annotations at Class and Method Level在类和方法级别使用 JAX-RS @Path 注释在 Java 中识别 URI
【发布时间】:2016-09-27 14:34:14
【问题描述】:

我在 Resource 类中创建了一种情况,在该情况下,我收到所有 3 个端点的 404,并希望有人可以帮助解释原因。这是我的资源类:

@Path("/incidents")
public class RegistrationResource {

    @GET
    @Path("/{incidentId}/registration")
    public Response getRegisteredIncidents(@PathParam("incidentId") String incidentId) {
           ...
    }

    @GET
    @Path("/registration/download")
    public Response downloadRegistrationIncidentsReport() {
           ...
    }

    @GET
    @Path("/registration/email")
    public Response emailRegistrationIncidentsReport() {
           ...
    }
}

我还使用 JAX-RS Application 类来注册我的资源并设置跨所有端点的基本“上下文”路径:

@ApplicationPath("/context") 
public class AdminApplication extends Application { 

    @Override
    public Set<Class<?>> getClasses() { 

        Set<Class<?>> resources = new HashSet<Class<?>>();

        resources.add(RegistrationResource.class);

        ...

        return resources;
    }
}

现在,如果我将这些方法分成多个资源类,我可以让每个服务都正常工作,但我的问题是为什么上面的设计不起作用?我试图按上述方法的顺序在这里调用的完整端点是:

/context/incidents/55/registration
/context/incidents/registration/download
/context/incidents/registration/email

为什么找不到上面的映射?通过这种设计,我得到了每个端点的 404。

org.apache.wink.server.internal.RequestProcessor logException 在调用处理程序链期间发生以下错误:WebApplicationException (404 - Not Found) with message 'null' while processing GET request sent to http://localhost:9081/someapp/context/incidents/55/registration

作为进一步的说明,我使用 Wink 1.1 作为我的 JAX-RS 实现,因为此时我绑定到 WebSphere 8.5.5.5 附带的打包版本。

感谢您的宝贵时间!

------------更新#1-----------

根据下面的 cmets,我修改了一些端点。但是,我仍然在某些端点上收到随机故障,出现 404 或有趣的 405 错误。重要的是要注意,如果我重新部署有时不同的端点会失败,或者可能所有的端点都会工作。然后,如果我再次重新部署,我将得到类似但不同的结果,某些端点失败或全部工作。底线是它非常不一致。

因此,与我的原始帖子相比,似乎有更多的端点发生冲突,所以为了完整起见,我将 所有 的端点粘贴到此处。

/* Public Service Set */

@ApplicationPath("/public") 
public class PublicApplication extends Application { 

    @Override
    public Set<Class<?>> getClasses() { 

        Set<Class<?>> resources = new HashSet<Class<?>>();

        resources.add(IncidentResource.class);
        resources.add(IncidentDetailsResource.class);
        resources.add(TicketResource.class);
        resources.add(RegistrationResource.class);

        return resources;
    }
}

@Path("/incidents")
public class IncidentResource { 

    @GET
    @Produces(MediaType.APPLICATION_JSON) 
    public Response getIncidents() throws Exception {
    //...
    } 

@Path("/")
public class IncidentDetailsResource {

    @GET
    @Path("/v1/incidents/{incidentId}/details")
    @Produces(MediaType.APPLICATION_JSON) 
    public Response getIncidentDetails(@PathParam("incidentId") String incidentId) throws Exception {
    //...
    }

    @GET
    @Path("/v2/incidents/{incidentId}/details")
    @Produces(MediaType.APPLICATION_JSON) 
    public Response getIncidentDetailsAsJson(@PathParam("incidentId") String incidentId) throws Exception {
    //...
    }

}

@Path("/incidents/{incidentId}/tickets") 
public class TicketResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getTickets(@PathParam("incidentId") String incidentId) throws Exception {
    //...
    }
}


@Path("/incidents")
public class RegistrationResource {

    @POST
    @Path("/{incidentId}/registration")
    @Consumes(MediaType.APPLICATION_JSON)
    public Response register(@PathParam("incidentId") String incidentId, BaseRequestMessage<IncidentRegistrationRequest> baseRequestMessage) throws Exception {
    //...
    }
}

/* Admin Service Set */

@ApplicationPath("/admin") 
public class AdminApplication extends Application { 

    @Override
    public Set<Class<?>> getClasses() { 

        Set<Class<?>> resources = new HashSet<Class<?>>();

        resources.add(AdminIncidentResource.class);
        resources.add(AdminIncidentDetailsResource.class);
        resources.add(AdminRegistrationResource.class);
        resources.add(AdminRegistrationReportResource.class);
        resources.add(AdminTicketResource.class);

        return resources;
    }
}

@Path("/incidents")
public class AdminIncidentResource { 

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response addIncident(BaseRequestMessage<IncidentRequest> baseRequestMessage) throws Exception {
    //...
    }

    @PUT 
    @Path("/{incidentId}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response updateIncident(@PathParam("incidentId") String incidentId, BaseRequestMessage<IncidentRequest> baseRequestMessage) throws Exception {
    //...
    }

    @DELETE 
    @Path("/{incidentId}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response deleteIncident(@PathParam("incidentId") String incidentId) throws Exception {
    //...
    }
}

@Path("/")
public class AdminIncidentDetailsResource {

    @PUT
    @Path("/v1/incidents/{incidentId}/details")
    @Consumes(MediaType.APPLICATION_JSON)
    public Response updateIncidentDetails(@PathParam("incidentId") String incidentId, BaseRequestMessage<IncidentDetailRequest> baseRequestMessage) throws Exception {
    //...    
    }

    @PUT
    @Path("/v2/incidents/{incidentId}/details")
    @Consumes(MediaType.APPLICATION_JSON)
    public Response updateIncidentDetailsAsJson(@PathParam("incidentId") String incidentId, BaseRequestMessage<JsonNode> baseRequestMessage) throws Exception {
    //...
    }
}

@Path("/incidents/{incidentId}/tickets")
public class AdminTicketResource {

    @POST
    @Consumes(MediaType.APPLICATION_JSON) 
    public Response addTicket(@PathParam("incidentId") String incidentId, BaseRequestMessage<TicketRequest> baseRequestMessage) throws Exception {
    //...   
    }

    @PUT
    @Consumes(MediaType.APPLICATION_JSON) 
    public Response updateTicket(@PathParam("incidentId") String incidentId, BaseRequestMessage<TicketRequest> baseRequestMessage) throws Exception {
    //...    
    }

    @DELETE
    @Path("/{detailsUrn}")
    @Consumes(MediaType.APPLICATION_JSON) 
    public Response removeTicket(@PathParam("incidentId") String incidentId, @PathParam("detailsUrn") String detailsUrn) throws Exception {
    //...    
    }

}

@Path("/incidents/registration/report")
public class AdminRegistrationReportResource {

    @GET
    @Path("/download")
    @Produces("application/vnd.ms-excel")
    public Response downloadRegistrationReport() throws Exception { 
    //...    
    }

    @GET
    @Path("/email")
    @Produces(MediaType.APPLICATION_JSON)
    public Response emailRegistrationReport() throws Exception { 
    //...    
    }
}

@Path("/incidents/{incidentId}/registration")
public class AdminRegistrationResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON) 
    public Response getRegisteredUsers(@PathParam("incidentId") String incidentId) throws Exception { 
    //...    
    }

    @PUT
    @Consumes(MediaType.APPLICATION_JSON)
    public Response updateRegisteredUser(@PathParam("incidentId") String incidentId, BaseRequestMessage<IncidentRegistrationRequest> baseRequestMessage) throws Exception {
    //...    
    }
}

为方便起见...上面的代码最终生成了这些 uri:

  • GET /public/incidents
  • GET /public/v1/incidents/{incidentId}/details
  • GET /public/v2/incidents/{incidentId}/details
  • GET /public/incidents/{incidentId}/tickets
  • POST /public/incidents/{incidentId}/registration

  • POST /admin/incidents

  • PUT /admin/incidents/{incidentId}
  • 删除 /admin/incidents/{incidentId}
  • PUT /admin/v1/incidents/{incidentId}/details
  • PUT /admin/v2/incidents/{incidentId}/details
  • POST /admin/incidents/{incidentId}/tickets
  • PUT /admin/incidents/{incidentId}/tickets
  • 删除 /admin/incidents/{incidentId}/tickets/{detailsUrn}
  • GET /admin/incidents/registration/report/download
  • GET /admin/incidents/registration/report/email
  • GET /admin/incidents/{incidentId}/registration
  • PUT /admin/incidents/{incidentId}/registration

代码很多;我本来想避免这种情况,但似乎有必要。此外,就其价值而言,在我添加 AdminRegistrationResource 类之前,一切似乎都在发挥作用。也许该类中某个端点的某些东西开始引起冲突?

再次...感谢您的宝贵时间!

------------更新#2-----------

我想我会在这里发布我最近的一个具体错误,希望它可以帮助解决问题。当我调用这个端点时:

GET /public/事件

我收到此错误消息:

00000203 ResourceRegis I org.apache.wink.server.internal.registry.ResourceRegistry filterDispatchMethods The system cannot find any method in the com.somewhere.unimportant.rest.resource.external.RegistrationResource class that supports GET. Verify that a method exists.

00000203 RequestProces I org.apache.wink.server.internal.RequestProcessor logException The following error occurred during the invocation of the handlers chain: WebApplicationException (405) with message 'null' while processing GET request sent to https://test.somewhere.com:88888/app777/public/events

现在更有趣的是,我在本地收到此错误,但我还将代码推送到有两个负载平衡服务器的测试环境。在测试环境中,我在其中一台服务器上始终收到此错误,但另一台服务器始终如一地工作。因此,在一台成功的服务器和一台不成功的服务器上分配 50% 的故障率。

奇怪...为什么它会在那个类中寻找我要命中的端点?

------------更新#3-----------

好的,我已经完全取消了在我的 PublicApplication 类中使用 @ApplicationPath 注释(如下面的 cmets 所建议的)并将其替换为仅仅“/”,但在以下情况下仍会收到 404 错误(请注意,对于此测试,我已完全删除“管理”服务):

/* Public (and only) Service Set */

@ApplicationPath("/") 
public class PublicApplication extends Application { 

    @Override
    public Set<Class<?>> getClasses() { 

        Set<Class<?>> resources = new HashSet<Class<?>>();

        resources.add(IncidentResource.class);
        resources.add(IncidentDetailsResource.class);
        resources.add(TicketResource.class);
        resources.add(RegistrationResource.class);

        return resources;
    }
}

@Path("/public")
public class IncidentResource { 

    @GET
    @Path("/incidents")
    @Produces(MediaType.APPLICATION_JSON) 
    public Response getIncidents() throws Exception {
    //...
    } 

@Path("/public")
public class IncidentDetailsResource {

    @GET
    @Path("/v1/incidents/{incidentId}/details")
    @Produces(MediaType.APPLICATION_JSON) 
    public Response getIncidentDetails(@PathParam("incidentId") String incidentId) throws Exception {
    //...
    }

    @GET
    @Path("/v2/incidents/{incidentId}/details")
    @Produces(MediaType.APPLICATION_JSON) 
    public Response getIncidentDetailsAsJson(@PathParam("incidentId") String incidentId) throws Exception {
    //...
    }

}

@Path("/public/incidents/{incidentId}/tickets") 
public class TicketResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getTickets(@PathParam("incidentId") String incidentId) throws Exception {
    //...
    }
}

@Path("/public/incidents/{incidentId}/registration")
public class RegistrationResource {

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response register(@PathParam("incidentId") String incidentId, BaseRequestMessage<IncidentRegistrationRequest> baseRequestMessage) throws Exception {
    //...
    }
}

在这种情况下,我收到此错误:

org.apache.wink.server.internal.RequestProcessor logException The following error occurred during the invocation of the handlers chain: WebApplicationException (404 - Not Found) with message 'null' while processing GET request sent to http://test.somewhere.com:88888/app777/public/events

尝试调用时:

GET /public/事件

上面的其他 4 个端点也可以正常工作。而且,更重要的是,如果我将 IncidentResource 类更改为如下所示(将方法级别的 @Path 注释完全移动到类级别),那么 所有 5 个端点工作:

@Path("/public/incidents")
public class IncidentResource { 

    @GET
    @Produces(MediaType.APPLICATION_JSON) 
    public Response getIncidents() throws Exception {
    //...
    } 

感谢所有陪伴我至今的人!

【问题讨论】:

  • 好主意。最初我 not 有斜线并添加它们,希望它们会有所帮助。但是,我确实按照您的建议再次删除了它们,但没有任何区别。
  • @Roman Vottner:在 JAX-RS Application 类的扩展中;我已经编辑了我的问题以包含上述内容。
  • 我希望这也能奏效。我肯定有一个 WebSphere 服务,在不同的路径上有两个不同的 @GET 方法,尽管我的两个方法都没有参数。您是否尝试过一些不同的路径/参数组合,只是为了尝试隔离特定原因?另外,什么版本的 WebSphere?您是否查看了 WebSphere 日志以了解启动时和收到请求时的错误消息?
  • 我有多种方法组合与其他路径/参数组合,这些都很好。启动时没有 WebSphere 错误,并且 WebSphere 的确切版本是 8.5.5.5。我也用收到的实际错误消息修改了原始问题。
  • 我只是在抓紧尝试的事情,在这里找出根本原因...我让您尝试删除 /id/registration 之一,并查看其他两个本身的行为方式是否相同?并尝试仅删除这两个无参数之一,看看会发生什么?我的直觉说这可能与无参数方法有关,但我没有这种预感的具体原因。

标签: java rest jax-rs websphere apache-wink


【解决方案1】:

删除 @ApplicationPath("/context") 或者不要在请求 uri 中使用 /context 或者只使用 @ApplicationPath("/") 它不会在升级时中断。

以上所有不同的方式都应该可以工作,但对于您的版本,我会推荐 @ApplicationPath("/") 并通过删除 /context 来更新您的请求 uri,一切都应该按照您最初的预期方式工作。

Apache Wink 尚不支持 ApplicationPath。

https://issues.apache.org/jira/browse/WINK-398

【讨论】:

  • 我没想到这是根本原因,但它显然是。如果我用@ApplicationPath("/") 替换@ApplicationPath("/context"),那么所有服务都可以工作。问题可能是我有两个单独的类扩展javax.ws.rs.Application - 本质上是“/contextOne”和“/contextTwo”。也许两者都存在冲突,将其中一个更改为简单的“/”可以消除冲突。令人不安的是,我们还有其他应用程序运行着多个Application 扩展,而且它们运行良好。它一定与两者之间的某些 url 组合有关?
  • 嗯...现在我认为它根本与双重应用程序扩展无关,因为如果我切换哪个注释具有“/”我仍然有同样的问题。我必须删除涉及上述资源的实际“contextOne”才能使其工作。因此,问题必须直接与 Wink 以及上述问题中描述的该资源中的组合有关。
  • 我认为解决应用程序路径注释是您目前唯一的选择。
  • 回到绘图板上。如果我删除 @Application("/context") 注释并在每个资源类中的所有 @Path 注释的开头添加“/context”我仍然有问题。这一定意味着资源类之间存在一些冲突。还在调查中……
  • 将“/context”添加到@Path 注释对我来说效果很好,但我没有和你一样的设置。如果您想让我看一下,请添加您的发现。
【解决方案2】:

这可能对你有用:

@Path("/incidents/{incidentId}/registration")
public class RegistrationResource1 {

    @GET
    public Response getRegisteredIncidents(@PathParam("incidentId") String incidentId) {
           ...
    }
}

@Path("/incidents/registration/download")
public class RegistrationResource {

    @GET
    public Response downloadRegistrationIncidentsReport() {
           ...
    }
}

但是,我不建议这样拆分控制器。相反,只需删除每个函数中尽可能多的代码以分离业务逻辑类。

【讨论】:

  • 谢谢迈克。是的,这实际上会起作用;正如我上面所说,我可以通过将它分成不同的类来让它工作。但我的问题是试图理解为什么原始实现不起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-24
  • 1970-01-01
  • 2016-02-07
相关资源
最近更新 更多