如何为 Swagger Codegen 处理 org.springframework.core.io.Resource
How to handle org.springframework.core.io.Resource for Swagger Codegen
我正在使用 Spring Boot 2 开发 Web 应用程序,它有 2 个 Spring 项目协同工作:一个是 REST API 服务器,通过 REST APIs,另一个是负责渲染网页和调用API服务器的web项目。 Web 项目使用 Swagger Codegen 自动生成 类 用于调用 APIs.
在 API 服务器中,我有一个控制器 ResourceController
,其端点用于提供文件内容(即下载文件),如下所示
@GetMapping("/files/{uuid}")
@ResponseBody
public org.springframework.core.io.Resource getFile(@PathVariable String uuid) {
String systemPath = fileService.getFilePath(uuid);
return new FileSystemResource(systemPath);
}
在 Web 客户端,Swagger 生成 ResourceControllerApi
,方法转换为
public io.swagger.client.model.Resource getFileUsingGET(String uuid) {...}
我想在 Web 项目中创建一个控制器,通过用户浏览器到 API 服务器之间的请求和响应。我试过了
@GetMapping("/client/files/{uuid}")
@ResponseBody
public io.swagger.client.model.Resource getFile(@PathVariable String uuid) {
return resourceControllerApi.getFileUsingGET(uuid);
}
调用 API(在客户端网络)时,出现此错误
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class io.swagger.client.model.Resource] and content type [image/jpeg]
我希望当我像 http://myweb/client/files/dee38be4-6ef9-460d-bc44-f1b93770ab83
一样输入 url 时,浏览器会下载文件内容。我一直在寻找一种将 io.swagger.client.model.Resource
转换为 org.springframework.core.io.Resource
的方法,但无法弄清楚。
以下是自动生成的内容io.swagger.client.model.Resource
/*
* NPA Marketplace REST API
* API to manage NPA Marketplace.
*
* OpenAPI spec version: 1.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
package io.swagger.client.model;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.client.model.InputStream;
import io.swagger.client.model.URI;
import io.swagger.client.model.URL;
/**
* Resource
*/
@javax.annotation.Generated(value = "io.swagger.codegen.languages.JavaClientCodegen", date = "2018-03-08T14:55:08.754+07:00")
public class Resource {
@JsonProperty("description")
private String description = null;
@JsonProperty("file")
private java.io.File file = null;
@JsonProperty("filename")
private String filename = null;
@JsonProperty("inputStream")
private InputStream inputStream = null;
@JsonProperty("open")
private Boolean open = null;
@JsonProperty("readable")
private Boolean readable = null;
@JsonProperty("uri")
private URI uri = null;
@JsonProperty("url")
private URL url = null;
public Resource description(String description) {
this.description = description;
return this;
}
/**
* Get description
* @return description
**/
@ApiModelProperty(value = "")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Resource file(java.io.File file) {
this.file = file;
return this;
}
/**
* Get file
* @return file
**/
@ApiModelProperty(value = "")
public java.io.File getFile() {
return file;
}
public void setFile(java.io.File file) {
this.file = file;
}
public Resource filename(String filename) {
this.filename = filename;
return this;
}
/**
* Get filename
* @return filename
**/
@ApiModelProperty(value = "")
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public Resource inputStream(InputStream inputStream) {
this.inputStream = inputStream;
return this;
}
/**
* Get inputStream
* @return inputStream
**/
@ApiModelProperty(value = "")
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
public Resource open(Boolean open) {
this.open = open;
return this;
}
/**
* Get open
* @return open
**/
@ApiModelProperty(value = "")
public Boolean isOpen() {
return open;
}
public void setOpen(Boolean open) {
this.open = open;
}
public Resource readable(Boolean readable) {
this.readable = readable;
return this;
}
/**
* Get readable
* @return readable
**/
@ApiModelProperty(value = "")
public Boolean isReadable() {
return readable;
}
public void setReadable(Boolean readable) {
this.readable = readable;
}
public Resource uri(URI uri) {
this.uri = uri;
return this;
}
/**
* Get uri
* @return uri
**/
@ApiModelProperty(value = "")
public URI getUri() {
return uri;
}
public void setUri(URI uri) {
this.uri = uri;
}
public Resource url(URL url) {
this.url = url;
return this;
}
/**
* Get url
* @return url
**/
@ApiModelProperty(value = "")
public URL getUrl() {
return url;
}
public void setUrl(URL url) {
this.url = url;
}
@Override
public boolean equals(java.lang.Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Resource resource = (Resource) o;
return Objects.equals(this.description, resource.description) &&
Objects.equals(this.file, resource.file) &&
Objects.equals(this.filename, resource.filename) &&
Objects.equals(this.inputStream, resource.inputStream) &&
Objects.equals(this.open, resource.open) &&
Objects.equals(this.readable, resource.readable) &&
Objects.equals(this.uri, resource.uri) &&
Objects.equals(this.url, resource.url);
}
@Override
public int hashCode() {
return Objects.hash(description, file, filename, inputStream, open, readable, uri, url);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("class Resource {\n");
sb.append(" description: ").append(toIndentedString(description)).append("\n");
sb.append(" file: ").append(toIndentedString(file)).append("\n");
sb.append(" filename: ").append(toIndentedString(filename)).append("\n");
sb.append(" inputStream: ").append(toIndentedString(inputStream)).append("\n");
sb.append(" open: ").append(toIndentedString(open)).append("\n");
sb.append(" readable: ").append(toIndentedString(readable)).append("\n");
sb.append(" uri: ").append(toIndentedString(uri)).append("\n");
sb.append(" url: ").append(toIndentedString(url)).append("\n");
sb.append("}");
return sb.toString();
}
/**
* Convert the given object to string with each line indented by 4 spaces
* (except the first line).
*/
private String toIndentedString(java.lang.Object o) {
if (o == null) {
return "null";
}
return o.toString().replace("\n", "\n ");
}
}
错误
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class io.swagger.client.model.Resource] and content type [image/jpeg]
是从行
发出的
ResponseEntity<T> responseEntity = restTemplate.exchange(requestEntity, returnType);
在 class io.swagger.client.ApiClient
中 auto-generated by Swagger Codegen of variant library resttemplate
, 即相当于 运行 命令 java -jar swagger-codegen-cli.jar generate -i api-specs.json -l java --library resttemplate
该错误表明 resttemplate 不知道如何将响应数据转换为 io.swagger.client.model.Resource
并提示我们创建合适的 HttpMessageConverter
。 This is a tutorial for creating a HttpMessageConverters and register them with the resttemplate.
然而,在我的例子中,HttpMessageConverter
不是一个解决方案,因为生成的 io.swagger.client.model.Resource
没有 属性 来保留来自 API 的文件数据回复。似乎 Swagger Codegen 误解了 API 服务器上的控制器会 return org.springframework.core.io.Resource
的 JSON 表示,从而生成对应部分 io.swagger.client.model.Resource
以接收 JSON 客户端的数据,但 API 服务器实际响应的是文件数据流,而不是 JSON.
我不知道这是否是 Swagger Codegen 的错误,或者我在某些方面做错了。
尽管如此,我决定放弃在 API 服务器上使用 org.springframework.core.io.Resource
。相反,我将控制器的 return 类型更改为 ResponseEntity<byte[]>
并手动配置响应 header 以具有正确的内容类型和文件名,如下所示
资源控制器(API服务器)
@GetMapping("/files/{uuid}")
@ResponseBody
public ResponseEntity<byte[]> getFile(@PathVariable String uuid) {
FileMetadata myFileData = fileService.getFileMetadata(uuid);
org.springframework.core.io.Resource res = new FileSystemResource(myFileData.getPaht());
byte[] data = IOUtils.toByteArray(res.getInputStream());
HttpHeaders respHeaders = new HttpHeaders();
respHeaders.setContentType(MediaType.valueOf(myFileData.getContentType()));
respHeaders.setContentLength(res.getFile().length());
respHeaders.setContentDispositionFormData("attachment", myFileData.getName());
return new ResponseEntity<>(data ,respHeaders, HttpStatus.OK);
}
然后,在 Web 客户端,我将从 API 服务器接收到的 byte[]
数据转换为 org.springframework.core.io.Resource
,并传递来自 [=57] 的响应 header =]服务器。
Web 客户端服务器上的控制器
@GetMapping("/client/files/{uuid}")
@ResponseBody
public ResponseEntity<org.springframework.core.io.Resource> getFile(@PathVariable String uuid) {
byte[] data = resourceControllerApi.getFileUsingGET(uuid);
HttpHeaders responseHeader = (HttpHeaders) RequestContextHolder.getRequestAttributes().getAttribute("responseHeader", RequestAttributes.SCOPE_REQUEST);
return new ResponseEntity<>(new ByteArrayResource(data), responseHeader, HttpStatus.OK);
}
关于我用来检索响应 header 的方式,因为我无法从生成的 api class 中获取响应 header,我有创建一个 ClientHttpRequestInterceptor
来获取 header 并将其作为请求属性。
public class ResponseHeaderClientRequestInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse response = execution.execute(request, body);
HttpHeaders responseHeader = response.getHeaders();
RequestAttributes requestAttrs = RequestContextHolder.getRequestAttributes();
if (requestAttrs != null) {
requestAttrs.setAttribute("responseHeader", responseHeader, RequestAttributes.SCOPE_REQUEST);
}
return response;
}
}
我正在使用 Spring Boot 2 开发 Web 应用程序,它有 2 个 Spring 项目协同工作:一个是 REST API 服务器,通过 REST APIs,另一个是负责渲染网页和调用API服务器的web项目。 Web 项目使用 Swagger Codegen 自动生成 类 用于调用 APIs.
在 API 服务器中,我有一个控制器 ResourceController
,其端点用于提供文件内容(即下载文件),如下所示
@GetMapping("/files/{uuid}")
@ResponseBody
public org.springframework.core.io.Resource getFile(@PathVariable String uuid) {
String systemPath = fileService.getFilePath(uuid);
return new FileSystemResource(systemPath);
}
在 Web 客户端,Swagger 生成 ResourceControllerApi
,方法转换为
public io.swagger.client.model.Resource getFileUsingGET(String uuid) {...}
我想在 Web 项目中创建一个控制器,通过用户浏览器到 API 服务器之间的请求和响应。我试过了
@GetMapping("/client/files/{uuid}")
@ResponseBody
public io.swagger.client.model.Resource getFile(@PathVariable String uuid) {
return resourceControllerApi.getFileUsingGET(uuid);
}
调用 API(在客户端网络)时,出现此错误
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class io.swagger.client.model.Resource] and content type [image/jpeg]
我希望当我像 http://myweb/client/files/dee38be4-6ef9-460d-bc44-f1b93770ab83
一样输入 url 时,浏览器会下载文件内容。我一直在寻找一种将 io.swagger.client.model.Resource
转换为 org.springframework.core.io.Resource
的方法,但无法弄清楚。
以下是自动生成的内容io.swagger.client.model.Resource
/*
* NPA Marketplace REST API
* API to manage NPA Marketplace.
*
* OpenAPI spec version: 1.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
package io.swagger.client.model;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.client.model.InputStream;
import io.swagger.client.model.URI;
import io.swagger.client.model.URL;
/**
* Resource
*/
@javax.annotation.Generated(value = "io.swagger.codegen.languages.JavaClientCodegen", date = "2018-03-08T14:55:08.754+07:00")
public class Resource {
@JsonProperty("description")
private String description = null;
@JsonProperty("file")
private java.io.File file = null;
@JsonProperty("filename")
private String filename = null;
@JsonProperty("inputStream")
private InputStream inputStream = null;
@JsonProperty("open")
private Boolean open = null;
@JsonProperty("readable")
private Boolean readable = null;
@JsonProperty("uri")
private URI uri = null;
@JsonProperty("url")
private URL url = null;
public Resource description(String description) {
this.description = description;
return this;
}
/**
* Get description
* @return description
**/
@ApiModelProperty(value = "")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Resource file(java.io.File file) {
this.file = file;
return this;
}
/**
* Get file
* @return file
**/
@ApiModelProperty(value = "")
public java.io.File getFile() {
return file;
}
public void setFile(java.io.File file) {
this.file = file;
}
public Resource filename(String filename) {
this.filename = filename;
return this;
}
/**
* Get filename
* @return filename
**/
@ApiModelProperty(value = "")
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public Resource inputStream(InputStream inputStream) {
this.inputStream = inputStream;
return this;
}
/**
* Get inputStream
* @return inputStream
**/
@ApiModelProperty(value = "")
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
public Resource open(Boolean open) {
this.open = open;
return this;
}
/**
* Get open
* @return open
**/
@ApiModelProperty(value = "")
public Boolean isOpen() {
return open;
}
public void setOpen(Boolean open) {
this.open = open;
}
public Resource readable(Boolean readable) {
this.readable = readable;
return this;
}
/**
* Get readable
* @return readable
**/
@ApiModelProperty(value = "")
public Boolean isReadable() {
return readable;
}
public void setReadable(Boolean readable) {
this.readable = readable;
}
public Resource uri(URI uri) {
this.uri = uri;
return this;
}
/**
* Get uri
* @return uri
**/
@ApiModelProperty(value = "")
public URI getUri() {
return uri;
}
public void setUri(URI uri) {
this.uri = uri;
}
public Resource url(URL url) {
this.url = url;
return this;
}
/**
* Get url
* @return url
**/
@ApiModelProperty(value = "")
public URL getUrl() {
return url;
}
public void setUrl(URL url) {
this.url = url;
}
@Override
public boolean equals(java.lang.Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Resource resource = (Resource) o;
return Objects.equals(this.description, resource.description) &&
Objects.equals(this.file, resource.file) &&
Objects.equals(this.filename, resource.filename) &&
Objects.equals(this.inputStream, resource.inputStream) &&
Objects.equals(this.open, resource.open) &&
Objects.equals(this.readable, resource.readable) &&
Objects.equals(this.uri, resource.uri) &&
Objects.equals(this.url, resource.url);
}
@Override
public int hashCode() {
return Objects.hash(description, file, filename, inputStream, open, readable, uri, url);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("class Resource {\n");
sb.append(" description: ").append(toIndentedString(description)).append("\n");
sb.append(" file: ").append(toIndentedString(file)).append("\n");
sb.append(" filename: ").append(toIndentedString(filename)).append("\n");
sb.append(" inputStream: ").append(toIndentedString(inputStream)).append("\n");
sb.append(" open: ").append(toIndentedString(open)).append("\n");
sb.append(" readable: ").append(toIndentedString(readable)).append("\n");
sb.append(" uri: ").append(toIndentedString(uri)).append("\n");
sb.append(" url: ").append(toIndentedString(url)).append("\n");
sb.append("}");
return sb.toString();
}
/**
* Convert the given object to string with each line indented by 4 spaces
* (except the first line).
*/
private String toIndentedString(java.lang.Object o) {
if (o == null) {
return "null";
}
return o.toString().replace("\n", "\n ");
}
}
错误
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class io.swagger.client.model.Resource] and content type [image/jpeg]
是从行
发出的ResponseEntity<T> responseEntity = restTemplate.exchange(requestEntity, returnType);
在 class io.swagger.client.ApiClient
中 auto-generated by Swagger Codegen of variant library resttemplate
, 即相当于 运行 命令 java -jar swagger-codegen-cli.jar generate -i api-specs.json -l java --library resttemplate
该错误表明 resttemplate 不知道如何将响应数据转换为 io.swagger.client.model.Resource
并提示我们创建合适的 HttpMessageConverter
。 This is a tutorial for creating a HttpMessageConverters and register them with the resttemplate.
然而,在我的例子中,HttpMessageConverter
不是一个解决方案,因为生成的 io.swagger.client.model.Resource
没有 属性 来保留来自 API 的文件数据回复。似乎 Swagger Codegen 误解了 API 服务器上的控制器会 return org.springframework.core.io.Resource
的 JSON 表示,从而生成对应部分 io.swagger.client.model.Resource
以接收 JSON 客户端的数据,但 API 服务器实际响应的是文件数据流,而不是 JSON.
我不知道这是否是 Swagger Codegen 的错误,或者我在某些方面做错了。
尽管如此,我决定放弃在 API 服务器上使用 org.springframework.core.io.Resource
。相反,我将控制器的 return 类型更改为 ResponseEntity<byte[]>
并手动配置响应 header 以具有正确的内容类型和文件名,如下所示
资源控制器(API服务器)
@GetMapping("/files/{uuid}")
@ResponseBody
public ResponseEntity<byte[]> getFile(@PathVariable String uuid) {
FileMetadata myFileData = fileService.getFileMetadata(uuid);
org.springframework.core.io.Resource res = new FileSystemResource(myFileData.getPaht());
byte[] data = IOUtils.toByteArray(res.getInputStream());
HttpHeaders respHeaders = new HttpHeaders();
respHeaders.setContentType(MediaType.valueOf(myFileData.getContentType()));
respHeaders.setContentLength(res.getFile().length());
respHeaders.setContentDispositionFormData("attachment", myFileData.getName());
return new ResponseEntity<>(data ,respHeaders, HttpStatus.OK);
}
然后,在 Web 客户端,我将从 API 服务器接收到的 byte[]
数据转换为 org.springframework.core.io.Resource
,并传递来自 [=57] 的响应 header =]服务器。
Web 客户端服务器上的控制器
@GetMapping("/client/files/{uuid}")
@ResponseBody
public ResponseEntity<org.springframework.core.io.Resource> getFile(@PathVariable String uuid) {
byte[] data = resourceControllerApi.getFileUsingGET(uuid);
HttpHeaders responseHeader = (HttpHeaders) RequestContextHolder.getRequestAttributes().getAttribute("responseHeader", RequestAttributes.SCOPE_REQUEST);
return new ResponseEntity<>(new ByteArrayResource(data), responseHeader, HttpStatus.OK);
}
关于我用来检索响应 header 的方式,因为我无法从生成的 api class 中获取响应 header,我有创建一个 ClientHttpRequestInterceptor
来获取 header 并将其作为请求属性。
public class ResponseHeaderClientRequestInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse response = execution.execute(request, body);
HttpHeaders responseHeader = response.getHeaders();
RequestAttributes requestAttrs = RequestContextHolder.getRequestAttributes();
if (requestAttrs != null) {
requestAttrs.setAttribute("responseHeader", responseHeader, RequestAttributes.SCOPE_REQUEST);
}
return response;
}
}