У меня есть служба 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
лучший способ обработки ошибок, таких как это?