Есть ли способ получить объект ответа об ошибке при использовании сгенерированного API веб-клиента для вызова веб-службы, которая дает сбой? - PullRequest
0 голосов
/ 20 февраля 2020

У меня есть служба REST, которая принимает идентификатор и две строки как json и возвращает идентификатор и две строки, объединенные как json. При наличии ошибок он может вернуть коды состояния 400, 404 и 500 с ответом об ошибке json в теле.

Swagger. json Файл выглядит следующим образом:

{
  "swagger": "2.0",
  "info": {
    "description": "---",
    "version": "---",
    "title": "---"
  },
  "basePath": "/rest",
  "tags": [
    {
      "name": "Concatenation resource"
    }
  ],
  "schemes": [
    "http",
    "https"
  ],
  "paths": {
    "/concatenation/{id}/concat": {
      "post": {
        "tags": [
          "Concatenation resource"
        ],
        "summary": "Concatenates two strings",
        "description": "",
        "operationId": "concatenate",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "in": "body",
            "name": "body",
            "required": false,
            "schema": {
              "$ref": "#/definitions/ConcatenationRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Strings successfully concatenated",
            "schema": {
              "$ref": "#/definitions/ConcatenationResponse"
            }
          },
          "400": {
            "description": "Invalid input",
            "schema": {
              "$ref": "#/definitions/ErrorResponse"
            }
          },
          "404": {
            "description": "Id does not exist",
            "schema": {
              "$ref": "#/definitions/ErrorResponse"
            }
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
  "definitions": {
    "ConcatenationResponse": {
      "type": "object",
      "required": [
        "id",
        "result"
      ],
      "properties": {
        "id": {
          "type": "string"
        },
        "result": {
          "type": "string"
        }
      }
    },
    "ErrorResponse": {
      "type": "object",
      "properties": {
        "errorCode": {
          "type": "string"
        },
        "errorMessage": {
          "type": "string"
        }
      }
    },
    "ConcatenationRequest": {
      "type": "object",
      "required": [
        "stringOne",
        "stringTwo"
      ],
      "properties": {
        "stringOne": {
          "type": "string",
          "description": "First string"
        },
        "stringTwo": {
          "type": "string",
          "description": "Second string"
        }
      }
    }
  }
}

I затем используйте плагин gradle org.openapi.generator version 4.2.3 для генерации клиентских классов API для веб-сервиса. Я использую следующую конфигурацию для плагина:

openApiGenerate {
    generatorName = "java"
    library = "webclient"
    inputSpec = "$projectDir/src/main/resources/api-spec/spec.json"
    outputDir = "$buildDir/generated"
    apiPackage = "com.test.rest.api"
    invokerPackage = "com.test.rest.invoker"
    modelPackage = "com.test.rest.model"
    modelNameSuffix = "MM"
    configOptions = [
            java11 : "true",
            dateLibrary: "java11",
            interfaceOnly: "true",
            useSwaggerAnnotations: "true"
    ]

    generateModelTests = false
    generateApiTests = false
}

Затем он генерирует эти классы:

@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2020-02-20T10:49:08.851266400+01:00[Europe/Paris]")
public class ConcatenationResourceApi {
    private ApiClient apiClient;

    public ConcatenationResourceApi() {
        this(new ApiClient());
    }

    @Autowired
    public ConcatenationResourceApi(ApiClient apiClient) {
        this.apiClient = apiClient;
    }

    public ApiClient getApiClient() {
        return apiClient;
    }

    public void setApiClient(ApiClient apiClient) {
        this.apiClient = apiClient;
    }

    /**
     * Concatenates two strings
     * 
     * <p><b>200</b> - Strings successfully concatenated
     * <p><b>400</b> - Invalid input
     * <p><b>404</b> - Id does not exist
     * <p><b>500</b> - Server error
     * @param id The id parameter
     * @param body The body parameter
     * @return ConcatenationResponseMM
     * @throws RestClientException if an error occurs while attempting to invoke the API
     */
    public Mono<ConcatenationResponseMM> concatenate(String id, ConcatenationRequestMM body) throws RestClientException {
        Object postBody = body;

        // verify the required parameter 'id' is set
        if (id == null) {
            throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'id' when calling concatenate");
        }

        // create path and map variables
        final Map<String, Object> pathParams = new HashMap<String, Object>();
        pathParams.put("id", id);


        final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
        final HttpHeaders headerParams = new HttpHeaders();
        final MultiValueMap<String, String> cookieParams = new LinkedMultiValueMap<String, String>();
        final MultiValueMap<String, Object> formParams = new LinkedMultiValueMap<String, Object>();

        final String[] accepts = { 
            "application/json"
        };
        final List<MediaType> accept = apiClient.selectHeaderAccept(accepts);
        final String[] contentTypes = { 
            "application/json"
        };
        final MediaType contentType = apiClient.selectHeaderContentType(contentTypes);

        String[] authNames = new String[] {  };

        ParameterizedTypeReference<ConcatenationResponseMM> returnType = new ParameterizedTypeReference<ConcatenationResponseMM>() {};
        return apiClient.invokeAPI("/concatenation/{id}/concat", HttpMethod.POST, pathParams, queryParams, postBody, headerParams, cookieParams, formParams, accept, contentType, authNames, returnType);
    }
}
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2020-02-20T10:49:08.851266400+01:00[Europe/Paris]")
public class ApiClient {
    [...]

    /**
     * Invoke API by sending HTTP request with the given options.
     *
     * @param <T> the return type to use
     * @param path The sub-path of the HTTP URL
     * @param method The request method
     * @param pathParams The path parameters
     * @param queryParams The query parameters
     * @param body The request body object
     * @param headerParams The header parameters
     * @param formParams The form parameters
     * @param accept The request's Accept header
     * @param contentType The request's Content-Type header
     * @param authNames The authentications to apply
     * @param returnType The return type into which to deserialize the response
     * @return The response body in chosen type
     */
    public <T> Mono<T> invokeAPI(String path, HttpMethod method, Map<String, Object> pathParams, MultiValueMap<String, String> queryParams, Object body, HttpHeaders headerParams, MultiValueMap<String, String> cookieParams, MultiValueMap<String, Object> formParams, List<MediaType> accept, MediaType contentType, String[] authNames, ParameterizedTypeReference<T> returnType) throws RestClientException {
        final WebClient.RequestBodySpec requestBuilder = prepareRequest(path, method, pathParams, queryParams, body, headerParams, cookieParams, formParams, accept, contentType, authNames);
        return requestBuilder.retrieve().bodyToMono(returnType);
    }

    [...]
}

Затем я использую сгенерированный клиентский API следующим образом:

ConcatenationRequestMM concatenationRequestMM = new ConcatenationRequestMM();
concatenationRequestMM.stringOne(concatenationRequest.getStringOne()).stringTwo(concatenationRequest.getStringTwo());
ConcatenationResponseMM concatenationResponseMM;
try {
    concatenationResponseMM = concatenationResourceApi
            .concatenate(id, concatenationRequestMM)
            .block();
    System.out.println("Id: " + concatenationResponseMM.getId() + ", result: " + concatenationResponseMM.getResult());
} catch (WebClientResponseException e) {
    String responseBodyAsString = e.getResponseBodyAsString();
    // How to get the response body unmarshalled into the ErrorResponseMM class? (getResponseBodyAsClass method does not exist)
    // ErrorResponseMM errorResponseMM = e.getResponseBodyAsClass(ErrorResponseMM.class);
    if (e.getRawStatusCode() == 400) {
        // Do some stuff here
    } else if (e.getRawStatusCode() == 404) {
        // Do some stuff here
    } else {
        // Do some stuff here
    }
}

Как получить объект unmarshalled ErrorResponseMM из ответа, когда веб-служба возвращает HTTP-статус 400, 404 или 500? Есть ли лучший способ обрабатывать ответы об ошибках с помощью webclient / webflux?

[EDIT] Будет работать только демонтаж строки ответа с помощью Джексона или Gson, но при этом используется WebClientResponseException лучший способ обработки ошибок, таких как это?

...