【问题标题】:WebFlux WebClient with Generic Types具有泛型类型的 WebFlux WebClient
【发布时间】:2020-11-15 01:57:10
【问题描述】:

我正在尝试使用 Spring WebFlux 中的 WebClient 创建一个客户端库。

服务器返回类似 JSON 的响应:

{
  "result": [
    {
      "parent": "",
      "made_sla": "true",
      "watch_list": "",
      "upon_reject": "cancel",
      "sys_updated_on": "2016-01-19 04:52:04",
      "approval_history": "",
      "number": "PRB0000050",
      "sys_updated_by": "glide.maint",
      "opened_by": {
        "link": "https://instance.service-now.com/api/now/table/sys_user/glide.maint",
        "value": "glide.maint"
      },
      "user_input": "",
      "sys_created_on": "2016-01-19 04:51:19",
      "sys_domain": {
        "link": "https://instance.service-now.com/api/now/table/sys_user_group/global",
        "value": "global"
      },
      "state": "4",
      "sys_created_by": "glide.maint",
      "knowledge": "false",
      "order": "",
      "closed_at": "2016-01-19 04:52:04",
      "cmdb_ci": {
        "link": "https://instance.service-now.com/api/now/table/cmdb_ci/55b35562c0a8010e01cff22378e0aea9",
        "value": "55b35562c0a8010e01cff22378e0aea9"
      },
      "delivery_plan": "",
      "impact": "3",
      "active": "false",
      "work_notes_list": "",
      "business_service": "",
      "priority": "4",
      "sys_domain_path": "/",
      "time_worked": "",
      "expected_start": "",
      "rejection_goto": "",
      "opened_at": "2016-01-19 04:49:47",
      "business_duration": "1970-01-01 00:00:00",
      "group_list": "",
      "work_end": "",
      "approval_set": "",
      "wf_activity": "",
      "work_notes": "",
      "short_description": "Switch occasionally drops connections",
      "correlation_display": "",
      "delivery_task": "",
      "work_start": "",
      "assignment_group": "",
      "additional_assignee_list": "",
      "description": "Switch occasionally drops connections",
      "calendar_duration": "1970-01-01 00:02:17",
      "close_notes": "updated firmware",
      "sys_class_name": "problem",
      "closed_by": "",
      "follow_up": "",
      "sys_id": "04ce72c9c0a8016600b5b7f75ac67b5b",
      "contact_type": "phone",
      "urgency": "3",
      "company": "",
      "reassignment_count": "",
      "activity_due": "",
      "assigned_to": "",
      "comments": "",
      "approval": "not requested",
      "sla_due": "",
      "comments_and_work_notes": "",
      "due_date": "",
      "sys_mod_count": "1",
      "sys_tags": "",
      "escalation": "0",
      "upon_approval": "proceed",
      "correlation_id": "",
      "location": ""
    },
    {
      "parent": "",
      "made_sla": "true",
      "watch_list": "",
      "upon_reject": "cancel",
      "sys_updated_on": "2016-01-19 04:52:04",
      "approval_history": "",
      "number": "PRB0000050",
      "sys_updated_by": "glide.maint",
      "opened_by": {
        "link": "https://instance.service-now.com/api/now/table/sys_user/glide.maint",
        "value": "glide.maint"
      },
      "user_input": "",
      "sys_created_on": "2016-01-19 04:51:19",
      "sys_domain": {
        "link": "https://instance.service-now.com/api/now/table/sys_user_group/global",
        "value": "global"
      },
      "state": "4",
      "sys_created_by": "glide.maint",
      "knowledge": "false",
      "order": "",
      "closed_at": "2016-01-19 04:52:04",
      "cmdb_ci": {
        "link": "https://instance.service-now.com/api/now/table/cmdb_ci/55b35562c0a8010e01cff22378e0aea9",
        "value": "55b35562c0a8010e01cff22378e0aea9"
      },
      "delivery_plan": "",
      "impact": "3",
      "active": "false",
      "work_notes_list": "",
      "business_service": "",
      "priority": "4",
      "sys_domain_path": "/",
      "time_worked": "",
      "expected_start": "",
      "rejection_goto": "",
      "opened_at": "2016-01-19 04:49:47",
      "business_duration": "1970-01-01 00:00:00",
      "group_list": "",
      "work_end": "",
      "approval_set": "",
      "wf_activity": "",
      "work_notes": "",
      "short_description": "Switch occasionally drops connections",
      "correlation_display": "",
      "delivery_task": "",
      "work_start": "",
      "assignment_group": "",
      "additional_assignee_list": "",
      "description": "Switch occasionally drops connections",
      "calendar_duration": "1970-01-01 00:02:17",
      "close_notes": "updated firmware",
      "sys_class_name": "problem",
      "closed_by": "",
      "follow_up": "",
      "sys_id": "04ce72c9c0a8016600b5b7f75ac67b5b",
      "contact_type": "phone",
      "urgency": "3",
      "company": "",
      "reassignment_count": "",
      "activity_due": "",
      "assigned_to": "",
      "comments": "",
      "approval": "not requested",
      "sla_due": "",
      "comments_and_work_notes": "",
      "due_date": "",
      "sys_mod_count": "1",
      "sys_tags": "",
      "escalation": "0",
      "upon_approval": "proceed",
      "correlation_id": "",
      "location": ""
    },
    {
      "parent": "",
      "made_sla": "true",
      "watch_list": "",
      "upon_reject": "cancel",
      "sys_updated_on": "2016-01-19 04:52:04",
      "approval_history": "",
      "number": "PRB0000050",
      "sys_updated_by": "glide.maint",
      "opened_by": {
        "link": "https://instance.service-now.com/api/now/table/sys_user/glide.maint",
        "value": "glide.maint"
      },
      "user_input": "",
      "sys_created_on": "2016-01-19 04:51:19",
      "sys_domain": {
        "link": "https://instance.service-now.com/api/now/table/sys_user_group/global",
        "value": "global"
      },
      "state": "4",
      "sys_created_by": "glide.maint",
      "knowledge": "false",
      "order": "",
      "closed_at": "2016-01-19 04:52:04",
      "cmdb_ci": {
        "link": "https://instance.service-now.com/api/now/table/cmdb_ci/55b35562c0a8010e01cff22378e0aea9",
        "value": "55b35562c0a8010e01cff22378e0aea9"
      },
      "delivery_plan": "",
      "impact": "3",
      "active": "false",
      "work_notes_list": "",
      "business_service": "",
      "priority": "4",
      "sys_domain_path": "/",
      "time_worked": "",
      "expected_start": "",
      "rejection_goto": "",
      "opened_at": "2016-01-19 04:49:47",
      "business_duration": "1970-01-01 00:00:00",
      "group_list": "",
      "work_end": "",
      "approval_set": "",
      "wf_activity": "",
      "work_notes": "",
      "short_description": "Switch occasionally drops connections",
      "correlation_display": "",
      "delivery_task": "",
      "work_start": "",
      "assignment_group": "",
      "additional_assignee_list": "",
      "description": "Switch occasionally drops connections",
      "calendar_duration": "1970-01-01 00:02:17",
      "close_notes": "updated firmware",
      "sys_class_name": "problem",
      "closed_by": "",
      "follow_up": "",
      "sys_id": "04ce72c9c0a8016600b5b7f75ac67b5b",
      "contact_type": "phone",
      "urgency": "3",
      "company": "",
      "reassignment_count": "",
      "activity_due": "",
      "assigned_to": "",
      "comments": "",
      "approval": "not requested",
      "sla_due": "",
      "comments_and_work_notes": "",
      "due_date": "",
      "sys_mod_count": "1",
      "sys_tags": "",
      "escalation": "0",
      "upon_approval": "proceed",
      "correlation_id": "",
      "location": ""
    }
  ]
}

result 字段包含元素数组,这些元素可能因所使用的 API 而异。

有关该 API 的更多信息是来自 ServiceNow 产品的Table API

正如您在文档中看到的那样,API 路径类似于:GET /now/table/{tableName},其中 tableName 可能是不同的值,结果元素拓扑将取决于获取的表。

因此结果字段中的响应元素可以由不同的名称或不同数量的属性组成。基本上它是一个通过 HTTP 公开的数据库,因此每个表都可以由不同数量的列组成。

我的 GET 实现:

public Mono<Result> get(){
    return client.get()
            .uri(uriBuilder -> uriBuilder
                    .path(this.tablePath)
                    .queryParam("active", "true")
                    .queryParam("state", "1")
                    .build())
            .accept(MediaType.APPLICATION_JSON)
            .retrieve()
            .bodyToMono(Result.class);
}

POJO

@Data
public class ProjectTask {

    private String project;

}
@Data
public class Result <T> {

    List<T> result;

}

获取错误的简单用法:

List<ProjectTask> projectTasks = Objects.requireNonNull(
        client.table("pm_project_task")
                .get().block())
        .getResult();

for (ProjectTask task : projectTasks){
    System.out.println("========PROJECT=============== "+task.getProject());
}

for (ProjectTask task : projectTasks){ 行我的运行时错误

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to my.namespace.api.services.servicenow.model.ProjectTask

响应需求:

  • 结果 POJO 必须使用通用类型来管理不同的响应元素类型。
  • 如果无法完成第一点,请尝试提供一种解决方案,通过针对不同表类型的可重用 GET 实现来获取该 API。
  • 我不想从客户端返回 List,我的想法是这个库只返回 Mono 或 Flux 对象。

【问题讨论】:

    标签: java spring-webflux servicenow spring-webclient servicenow-rest-api


    【解决方案1】:

    您需要使用 parameterizedtypereference 将 json 反序列化为 Result 的确切类型。否则 Spring 将任何未知类型反序列化为 LinkedHashMap。 例如。

      public Mono<Result<ProjectTask>> get(){
        return client.get()
            .uri(uriBuilder -> uriBuilder
                .path(this.tablePath)
                .queryParam("active", "true")
                .queryParam("state", "1")
                .build())
            .accept(MediaType.APPLICATION_JSON)
            .retrieve()
            .bodyToMono(new ParameterizedTypeReference<Result<ProjectTask>>() {
            });
      }
    
    

    如果你想让get这个方法成为一个通用方法,它适用于所有类型的Result,那么你应该看看this stackoverflow question

    【讨论】:

    • 在我这边运行,也提供链接上的通用建议,准确地说,在调用部分,需要它声明一个变量来获取 Mono 值,然后调用 getResult()在初始化另一个 List 类型的变量时,否则会出现编译错误,因为需要 List 而不是 List。也许我们可以改进这一点,但我不知道如何。
    • 我已取消验收检查,目前无法使用链接解决方案。在 ParameterizedTypeReference 扩展中创建一个包含多个类的列表,在 Spring Framework 项目github.com/spring-projects/spring-framework/issues/20643的 sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.validateConstructorArguments(ParameterizedTypeImpl.java:58) 增强处抛出 java.lang.reflect.MalformedParameterizedTypeException >
    • @OscarRP 如果泛型和参数化类型引用存在问题,我建议您使用继承。让 Projecttask 和其他 pojo 继承一个公共父类并在您的 Web 客户端响应中使用它。
    猜你喜欢
    • 2019-04-22
    • 1970-01-01
    • 1970-01-01
    • 2020-08-26
    • 2015-07-09
    • 2020-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多