简体   繁体   English

如何使用 Swagger 代码生成器开发一个简单的 REST 客户端?

[英]How to develop a simple REST Client using Swagger codegen?

I'm learning about Swagger and how to generate REST Client using Swagger codegen.我正在学习 Swagger 以及如何使用 Swagger codegen 生成 REST 客户端。 I know how to do documentation with Swagger, also I know how to generate a simple REST Server with Swagger, but I don't know how to generate a simple REST Client with Swagger codegen.我知道如何用 Swagger 做文档,我也知道如何用 Swagger 生成一个简单的 REST 服务器,但我不知道如何用 Swagger codegen 生成一个简单的 REST 客户端。

For example, I have a simple app, it is a REST Server and I want to generate REST Client.例如,我有一个简单的应用程序,它是一个 REST 服务器,我想生成 REST 客户端。 Can I do that with Swagger codegen?我可以用 Swagger codegen 做到这一点吗?

The controller for the REST Server: REST 服务器的 controller:

package com.dgs.spring.springbootswagger.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;

@RestController
@RequestMapping("/api/v1")
@Api(value = "Employee Management System", description = "Operations pertaining to employee in Employee Management System")
public class EmployeeController {

     @Autowired
     private EmployeeRepository employeeRepository;

        @ApiOperation(value = "View a list of available employees", response = List.class)
        @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successfully retrieved list"),
            @ApiResponse(code = 401, message = "You are not authorized to view the resource"),
            @ApiResponse(code = 403, message = "Accessing the resource you were trying to reach is forbidden"),
            @ApiResponse(code = 404, message = "The resource you were trying to reach is not found")
        })
     @GetMapping("/employees")
     public List<Employee> getAllEmployees() {
         return employeeRepository.findAll();
     }

     @ApiOperation(value = "Get an employee by Id")   
     @GetMapping("/employees/{id}")
     public ResponseEntity<Employee> getEmployeeById(
             @ApiParam(value = "Employee id from which employee object will retrieve", required = true) @PathVariable(value = "id") Long employeeId)
             throws ResourceNotFoundException {

          Employee employee = employeeRepository.findById(employeeId)
            .orElseThrow(() -> new ResourceNotFoundException("Employee not found for this id :: " + employeeId));

          return ResponseEntity.ok().body(employee);
     }

     @ApiOperation(value = "Add an employee")
     @PostMapping("/employees")
     public Employee createEmployee(
             @ApiParam(value = "Employee object store in database table", required = true) @Valid @RequestBody Employee employee) {
         return employeeRepository.save(employee);
     }

     @ApiOperation(value = "Update an employee")
     @PutMapping("/employees/{id}")
     public ResponseEntity<Employee> updateEmployee(
             @ApiParam(value = "Employee Id to update employee object", required = true) @PathVariable(value = "id") Long employeeId,
             @ApiParam(value = "Update employee object", required = true) @Valid @RequestBody Employee employeeDetails) throws ResourceNotFoundException {

          Employee employee = employeeRepository.findById(employeeId)
            .orElseThrow(() -> new ResourceNotFoundException("Employee not found for this id :: " + employeeId));

          employee.setEmail(employeeDetails.getEmail());
          employee.setLastName(employeeDetails.getLastName());
          employee.setFirstName(employeeDetails.getFirstName());
          final Employee updatedEmployee = employeeRepository.save(employee);

          return ResponseEntity.ok(updatedEmployee);
     }

     @ApiOperation(value = "Delete an employee")
     @DeleteMapping("/employees/{id}")
     public Map<String, Boolean> deleteEmployee(
             @ApiParam(value = "Employee Id from which employee object will delete from database table", required = true) @PathVariable(value = "id") Long employeeId)
       throws ResourceNotFoundException {

      Employee employee = employeeRepository.findById(employeeId)
        .orElseThrow(() -> new ResourceNotFoundException("Employee not found for this id :: " + employeeId));

      employeeRepository.delete(employee);
      Map<String, Boolean> response = new HashMap<>();
      response.put("deleted", Boolean.TRUE);

      return response;
     }
}

After that I've developed a simple REST Client:之后我开发了一个简单的 REST 客户端:

package com.dgs.restclient.controllers;

@Controller
public class UpdateController {

    @Autowired
    private EmployeeRestClient restClient;

    @GetMapping("/showStartUpdate")
    public String showStartCheckin() {
        return "startUpdate";
    }

    @PostMapping("/startUpdate")
    public String startCheckIn(@RequestParam("employeeId") Long employeeId, ModelMap modelMap) {

        Employee employee = restClient.findEmployee(employeeId);
        modelMap.addAttribute("employee", employee);

        return "displayEmployeeDetails";
    }

    @PostMapping("/completeUpdate")
    public String completeCheckIn(@RequestParam("employeeId") Long employeeId,
            @RequestParam("employeeFirstName") String employeeFirstName,
            @RequestParam("employeeLastName") String employeeLastName,
            @RequestParam("employeeEmail") String employeeEmail) {

        EmployeeUpdateRequest employeeUpdateRequest = new EmployeeUpdateRequest();
        employeeUpdateRequest.setId(employeeId);
        employeeUpdateRequest.setFirstName(employeeFirstName);
        employeeUpdateRequest.setLastName(employeeLastName);
        employeeUpdateRequest.setEmail(employeeEmail);
        restClient.updateEmployee(employeeUpdateRequest);

        return "updateConfirmation";
    }

}

The EmployeeRestClient: EmployeeRestClient:

package com.dgs.restclient.integration;

@Component
public class EmployeeRestClientImpl implements EmployeeRestClient {

    private static final String EMPLOYEE_REST_URL = 
            "http://localhost:8080/api/v1/employees/";

    @Override
    public Employee findEmployee(Long id) {

        RestTemplate restTemplate = new RestTemplate();
        Employee employee = restTemplate
                .getForObject(EMPLOYEE_REST_URL + id, Employee.class);

        return employee;
    }

    @Override
    public Employee updateEmployee(EmployeeUpdateRequest request) {

        RestTemplate restTemplate = new RestTemplate();
        restTemplate
                .put(EMPLOYEE_REST_URL + request.getId(), request, Employee.class); 

        Employee employee = restTemplate
                .getForObject(EMPLOYEE_REST_URL + request.getId(), Employee.class);

        return employee;
    }

}

This REST Client is developed by me and I want to know if I can do this REST Client development with Swagger codegen and how?这个 REST 客户端是我开发的,我想知道我是否可以用 Swagger codegen 做这个 REST 客户端开发,怎么做? Do I need just to add the swagger-codegen-maven-plugin in the pom.xml?我是否只需要在 pom.xml 中添加 swagger-codegen-maven-plugin? I've heard about adding this plugin and a yml file and Swagger will create the REST Client.我听说过添加这个插件和一个 yml 文件,Swagger 将创建 REST 客户端。 Any feedback will be appreciated!任何反馈将不胜感激!

Updated: 更新:

Your question was answered in another post. 您的问题已在另一篇文章中得到解答 Take a look at: related post 看看: 相关帖子

... ...

FYI a simple approach using command line: 仅供参考使用命令行的简单方法:

There is a good tutorial at baeldung about it: how to create rest client with swagger codegen baeldung有一个很好的教程: 如何使用swagger codegen 创建rest客户端

Eg Execute command: 例如执行命令:

java -jar swagger-codegen-cli.jar generate \
  -i http://mydomain/v2/swagger.json \
  --api-package com.mypackage.api \
  --model-package com.mypackage.model \
  --invoker-package com.mypackage.invoker \
  --group-id com.mygroup \
  --artifact-id spring-swagger-codegen-api-client \
  --artifact-version 0.0.1-SNAPSHOT \
  -l java \
  --library resttemplate \
  -o spring-swagger-codegen-api-client

Swagger Codegen supports the following Client implementations: Swagger Codegen支持以下客户端实现:

  1. jersey1 + Jackson jersey1 +杰克逊
  2. Jersey2 + Jackson 泽西2 +杰克逊
  3. Feign + Jackson 假装+杰克逊
  4. OkHttp + Gson OkHttp + Gson
  5. Retrofit2/OkHttp + Gson Retrofit2 / OkHttp + Gson
  6. Spring RestTemplate + Jackson Spring RestTemplate + Jackson
  7. Resteasy + Jackson Resteasy + Jackson

PS As you can see, the rest client is generated from swagger spec definition and it is definided with “-i” argument. PS正如您所看到的,其余客户端是通过swagger规范定义生成的,并且使用“-i”参数进行定义。

Yes. 是。 You can use swagger-codegen-maven-plugin to generate a REST client. 您可以使用swagger-codegen-maven-plugin生成REST客户端。 But before that , you need to describe the REST API in YAML or JSON in OpenAPI Specification mainly because swagger-codegen-maven-plugin only can generate a REST client from a file written in this specification. 但在此之前,您需要在OpenAPI Specification描述YAML或JSON中的REST API,主要是因为swagger-codegen-maven-plugin只能从本规范中编写的文件生成REST客户端。

Other answers assume that you need to write the specification manually while my solution take a step further to automatically generating the specification from the REST controller source codes. 其他答案假设您需要手动编写规范,而我的解决方案更进一步从REST控制器源代码自动生成规范。

The latest OpenAPI version is 3.0 .But based on the package of your imported swagger annotation , you are using version 2.0 (or before). 最新的OpenAPI版本是3.0。但是基于导入的swagger注释的包,您使用的是2.0版(或之前版本)。 So my solution assume you are using OpenAPI 2.0. 所以我的解决方案假设您正在使用OpenAPI 2.0。

Generating Open API Specification 生成开放API规范

First, you can use swagger-maven-plugin to generate an OpenAPI spec from the RestController source codes. 首先,您可以使用swagger-maven-plugin从RestController源代码生成OpenAPI规范。 It basically analyses the Swagger annotations annotated in the @RestController classes that specified in <locations> and dump the OpenAPI spec to /src/main/resources/swagger.json : 它基本上分析了在<locations>中指定的@RestController类中注释的Swagger注释,并将OpenAPI规范转储到/src/main/resources/swagger.json

<plugin>
    <groupId>com.github.kongchen</groupId>
    <artifactId>swagger-maven-plugin</artifactId>
    <version>3.1.5</version>
    <configuration>
        <apiSources>
            <apiSource>
                <springmvc>true</springmvc>
                <locations>
                    <location>com.dgs.spring.springbootswagger.controller.EmployeeController</location>
                    <location>com.dgs.spring.springbootswagger.controller.FooController</location>
                </locations>
                <schemes>
                    <scheme>http</scheme>
                </schemes>
                <host>127.0.0.1:8080</host>
                <basePath>/</basePath>
                <info>
                    <title>My API</title>
                    <version>1.1.1</version>
                </info>
                <swaggerDirectory>${basedir}/src/main/resources/</swaggerDirectory>
            </apiSource>
        </apiSources>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Execute the followings maven command to start generation: 执行以下maven命令开始生成:

mvn clean compile

Generating Rest Client 生成Rest客户端

After swagger.json is generated, you can copy and paste it to your client project (eg /src/main/resources/swagger.json) . 生成swagger.json后,您可以将其复制并粘贴到客户端项目中(例如/src/main/resources/swagger.json)。 We can then use swagger-codegen-maven-plugin to generate a HTTP client . 然后我们可以使用swagger-codegen-maven-plugin来生成HTTP客户端。

By default , it will generate the whole maven project which includes test cases and other documentation stuff. 默认情况下,它将生成整个maven项目 ,其中包括测试用例和其他文档内容。 But what I want is just the HttpClient 's source codes without other things. 但我想要的只是HttpClient的源代码而没有其他东西。 After several trial and error , I settle down to the following configuration : 经过多次反复试验,我确定了以下配置:

<plugin>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-codegen-maven-plugin</artifactId>
    <version>2.4.7</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <inputSpec>${basedir}/src/main/resources/swagger.json</inputSpec>
                <language>java</language>
                <library>resttemplate</library>
                <output>${project.basedir}/target/generated-sources/</output>

                <apiPackage>com.example.demo.restclient.api</apiPackage>
                <modelPackage>com.example.demo.restclient.model</modelPackage>
                <invokerPackage>com.example.demo.restclient</invokerPackage>

                <generateApiTests>false</generateApiTests>
                <generateModelTests>false</generateModelTests>
                <generateApiDocumentation>false</generateApiDocumentation>
                <generateModelDocumentation>false</generateModelDocumentation>
                <configOptions>
                    <dateLibrary>java8</dateLibrary>
                    <sourceFolder>restclient</sourceFolder>
                </configOptions>
            </configuration>
        </execution>
    </executions>
</plugin>

The generated HTTP client is based on RestTemplate and will be generated to the folder target/generated-sources/restclient . 生成的HTTP客户端基于RestTemplate,将生成到文件夹target/generated-sources/restclient You may have to configure your IDE to import the generated client in order to use it . 您可能必须配置IDE以导入生成的客户端才能使用它。 (In case of Eclipse, you can configure in Project Properties ➡️ Java Build Path ➡️ Add the folder of the generated rest client) (在Eclipse的情况下,您可以在项目属性中配置➡️JavaBuildPath➡️添加生成的rest客户端的文件夹)

To start generating the client , just execute the maven command : 要开始生成客户端,只需执行maven命令:

mvn clean compile

To use the generated HTTP client : 要使用生成的HTTP客户端:

ApiClient apiClient = new ApiClient();

//Override the default API base path configured in Maven
apiClient.setBasePath("http://api.example.com/api");

EmployeeManagementSystemApi api = new EmployeeManagementSystemApi(apiClient);
api.getEmployeeById(1l);

Note : 注意 :

  • If you come across javax/xml/bind/annotation/XmlRootElement exception during generation when using java8+ , you may need to refer to this . 如果在使用java8 +时生成期间遇到javax/xml/bind/annotation/XmlRootElement异常,则可能需要参考此内容

1) Go to https://editor.swagger.io create your swagger documentation, i am using "Swagger Petstore" as example 1)转到https://editor.swagger.io创建您的swagger文档,我使用“Swagger Petstore”作为示例

2) Now select File, Import File and upload the downloaded swagger.json file 2)现在选择File,Import File并上传下载的swagger.json文件

3) Open https://swagger.io/tools/swagger-codegen/ 3)打开https://swagger.io/tools/swagger-codegen/

4) Use following steps : 4)使用以下步骤:

i) Clone the repository to disk git clone https://github.com/swagger-api/swagger-codegen.git i)将存储库克隆到磁盘git clone https://github.com/swagger-api/swagger-codegen.git

ii) Run mvn clean package ii)运行mvn clean包

iii) Copy the swagger-codegen-cli.jar file from the target folder to a local drive on your computer. iii)将swagger-codegen-cli.jar文件从目标文件夹复制到计算机上的本地驱动器。

iv) Next execute the following command to generate a client: iv)接下来执行以下命令以生成客户端:

     java -jar swagger-codegen-cli.jar -i <json_file> -l python -o my_client

There are three arguments for this command: 此命令有三个参数:

 -i Specifies the path to the input file. This can be a URL

 -l Specifies the programming language for the client

 -o Specifies the output directory where the generate code should be located

Swagger Codegen is an open source project which allows generation of API client libraries (SDK generation), server stubs, and documentation automatically from an OpenAPI Specification. Swagger Codegen是一个开源项目,它允许从OpenAPI规范自动生成API客户端库(SDK生成),服务器存根和文档。 Swagger Codegen is available for download in the GitHub repository, or can be generated for any new or existing OpenAPI-defined API in the integrated SwaggerHub platform. Swagger Codegen可以在GitHub存储库中下载,也可以在集成的SwaggerHub平台中为任何新的或现有的OpenAPI定义的API生成。 SwaggerHub brings the Swagger Editor, UI, and Codegen tools to the cloud in an integrated API design and documentation, built for API teams working with the Swagger (OpenAPI) specification. SwaggerHub将Swagger编辑器,UI和Codegen工具集成到一个集成的API设计和文档中,为使用Swagger(OpenAPI)规范的API团队构建。

There are plugins for build tools like Maven and Gradle, since already given on few answerso not adding here 有一些用于构建工具的插件,如Maven和Gradle,因为已经给出了很少的答案,而不是在这里添加

Swagger Endpoints Swagger端点

Let's say your application's Swagger endpoints can be accessed at: 假设您的应用程序的Swagger端点可以在以下位置访问:

  1. Testing Swagger 2.0 JSON API documentation 测试Swagger 2.0 JSON API文档

    http://localhost:8080/v2/api-docs?group=employee HTTP://本地主机:8080 / V2 / API-文档组=雇员

    http://localhost:8080/v2/api-docs (if you haven't set a group named employee ) http:// localhost:8080 / v2 / api-docs (如果您尚未设置名为employee的组)

  2. Testing Swagger UI 测试Swagger UI

    http://localhost:8080/swagger-ui.html HTTP://本地主机:8080 /招摇,ui.html

Download Swagger Codegen Executable 下载Swagger Codegen可执行文件

You can download the swagger-codegen-cli-2.4.7.jar from the Maven Central Repository. 您可以从Maven Central Repository下载swagger-codegen-cli-2.4.7.jar

Generating Client Code 生成客户端代码

Now that you have the Swagger Codegen JAR, you can generate the REST client by executing the following command: 现在您已经拥有了Swagger Codegen JAR,您可以通过执行以下命令来生成REST客户端:

java -jar swagger-codegen-cli-2.4.7.jar generate \
  -i http://localhost:8080/v2/api-docs?group=employee \
  -l java \
  -o swagger-codegen-client

if no swagger grouping, 如果没有招摇的分组,

java -jar swagger-codegen-cli-2.4.7.jar generate \
  -i http://localhost:8080/v2/api-docs \
  -l java \
  -o swagger-codegen-client

Options 选项

Though Swagger Codegen CLI comes with a number of options, we're using the options which are absolutely necessary for generating the client code. 虽然Swagger Codegen CLI提供了许多选项,但我们使用的选项对于生成客户端代码是绝对必要的。

  • -i the URL pointing to your application's Swagger api docs . -i指向应用程序的Swagger api docs的URL。
  • -l the programming language of the client which in this case is java -l客户端的编程语言,在本例中是java
  • -o the output folder for the generated client code. -o生成的客户端代码的输出文件夹。

Once you execute the previous command for generating the code, you should notice notice the following message on your terminal: 执行上一个生成代码的命令后,您应该注意到终端上的以下消息:

[main] INFO io.swagger.parser.Swagger20Parser - reading from http://localhost:8080/v2/api-docs?group=employee
[main] WARN io.swagger.codegen.ignore.CodegenIgnoreProcessor - Output directory does not exist, or is inaccessible. No file (.swagger-codegen-ignore) will be evaluated.
[main] INFO io.swagger.codegen.AbstractGenerator - writing file swagger-codegen-client/src/main/java/io/swagger/client/model/Employee.java
[main] INFO io.swagger.codegen.AbstractGenerator - writing file swagger-codegen-client/docs/Employee.md
[main] INFO io.swagger.codegen.AbstractGenerator - writing file swagger-codegen-client/src/main/java/io/swagger/client/api/EmployeeControllerApi.java
...
[main] INFO io.swagger.codegen.AbstractGenerator - writing file swagger-codegen-client/src/main/java/io/swagger/client/ApiClient.java
...

REST Client Project REST客户端项目

Once the code generation is completed, you should notice a gradle/maven project with the following structure: 代码生成完成后,您应该注意到具有以下结构的gradle/maven项目:

__ swagger-codegen-client
  |__ README.md
  |__ build.gradle
  |__ build.sbt
  |__ docs
  |__ git_push.sh
  |__ gradle
  |__ gradle.properties
  |__ gradlew
  |__ gradlew.bat
  |__ pom.xml
  |__ settings.gradle
  |__ src
     |__ main
        |__ java
          |__ io.swagger.client.api
             |__ EmployeeControllerApi.java
     |__ test
        |__ java
          |__ io.swagger.client.api
             |__ EmployeeControllerApiTest.java

An example of a generated client project can be found here . 可以在此处找到生成的客户端项目的示例。

Using the REST Client 使用REST客户端

The client project contains lot of java classes. 客户端项目包含许多java类。 However the most important class is the EmployeeControllerApi.java . 但是最重​​要的类是EmployeeControllerApi.java This's the class which contains all the logic for making REST client classes. 这是包含用于创建REST客户端类的所有逻辑的类。

The other important class is EmployeeControllerApiTest.java . 另一个重要的类是EmployeeControllerApiTest.java It shows you how to use the EmployeeControllerApi.java . 它向您展示了如何使用EmployeeControllerApi.java The generated client project also provides a README file which is very helpful. 生成的客户端项目还提供了一个非常有用的README文件。

URL Changes 网址更改

The ApiClient class contains information related to establishing a HTTP client connection. ApiClient类包含与建立HTTP客户端连接相关的信息。 Please make sure the basePath to your REST application is correct. 请确保您的REST应用程序的basePath正确无误。 In the generated example, the basePath had a https://localhost:8080 URL instead of http://localhost:8080 . 在生成的示例中, basePath具有https://localhost:8080 URL而不是http://localhost:8080

Java 12 Changes Java 12更改

The generated project works well with Java 8. If you are using Java 12, you'll have to add the following dependencies to make the project compile: 生成的项目适用于Java 8.如果您使用的是Java 12,则必须添加以下依赖项才能使项目编译:

    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.0</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-core</artifactId>
        <version>2.3.0</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-impl</artifactId>
        <version>2.3.0</version>
    </dependency>

    <dependency>
        <groupId>javax.annotation</groupId>
        <artifactId>javax.annotation-api</artifactId>
        <version>1.3.2</version>
    </dependency>

Example REST Call 示例REST调用

Here's an example of creating an employee by making a REST POST method method call. 以下是通过进行REST POST方法方法调用来创建employee的示例。

Employee employee = new Employee();
employee.setId(3L);
employee.setFirstName("Sam");
employee.setLastName("Fox");
employee.setEmail("sfox@gmail.com");

EmployeeControllerApi api = new EmployeeControllerApi();
Employee response = api.createEmployeeUsingPOST(employee);
System.out.println(response);

You should a response similar to this: 您应该回复类似于:

class Employee {
    email: sfox@gmail.com
    firstName: Sam
    id: 3
    lastName: Fox
}

You can find a complete example here . 你可以在这里找到一个完整的例子。

just add a swagger plugin does not generate a rest client you need to follow these steps as below. 只需添加一个swagger插件不会生成休息客户端,您需要按照以下步骤操作。

write down the specification in YAML format. 以YAML格式记下规范。 Based on the specification outcome, will be generated. 基于规范结果,将生成。 Save the specification as YAML file. 将规范保存为YAML文件。 It will be saved as swagger.yaml follow the tutorial: https://howtodoinjava.com/swagger2/code-generation-for-rest-api/ 它将保存为swagger.yaml,请遵循以下教程: https ://howtodoinjava.com/swagger2/code-generation-for-rest-api/

I struggled a lot with this and ended up making my own tool.我为此苦苦挣扎,最终制作了自己的工具。 It is really small and depends only on spring-web .它真的很小,只依赖于spring-web I even filed a PR to see if it can be part of Spring.我什至 提交了一份 PR ,看看它是否可以成为 Spring 的一部分。

I call it Spring RestTemplate Client and it does what Feign and other tools does, but much more lightweight and only supports Spring.我称它为Spring RestTemplate Client ,它可以完成Feign和其他工具的功能,但更轻巧,仅支持 Spring。

final MyApiInterface myClient = SpringRestTemplateClientBuilder
  .create(MyApiInterface.class)
  .setUrl(this.getMockUrl())
  .setRestTemplate(restTemplate)         // Optional
  .setHeader("header-name", "the value") // Optional
  .setHeaders(HttpHeaders)               // Optional
  .build();

Any call on myClient will be translated to a HTTP call using the annotations of MyApiInterface :使用 MyApiInterface 的注释,对myClient的任何调用都将转换为MyApiInterface调用:

final ResponseEntity<MyDTO> response = myClient.getMyDto();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM