Merge pull request #534 from anhthidao/OpenApi
Implement code to generate route handler code from a JSON file
This commit is contained in:
Коммит
21cbde6673
|
@ -0,0 +1,318 @@
|
|||
using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi.Readers;
|
||||
|
||||
namespace CodeGenerator;
|
||||
|
||||
public class App
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Please enter two arguments: an input file path and an output file path.");
|
||||
Environment.Exit(1); // Code 1 is for problems with passed in file paths
|
||||
}
|
||||
|
||||
var document = ReadJson(args[0]);
|
||||
var paths = document?.Paths;
|
||||
|
||||
if (paths is null || paths.Count == 0)
|
||||
{
|
||||
Console.Error.WriteLine("No path were found in the schema.");
|
||||
Environment.Exit(2); // Code 2 is for problems with paths in schema
|
||||
}
|
||||
|
||||
var fileProperties = new Dictionary<string, Dictionary<string, Dictionary<string, string?>>>();
|
||||
|
||||
foreach (var path in paths)
|
||||
{
|
||||
var operations = path.Value.Operations;
|
||||
if (operations is null || operations.Count == 0)
|
||||
{
|
||||
Console.Error.WriteLine("No operation was found in path.");
|
||||
Environment.Exit(3); // Code 3 is for problems with operations
|
||||
}
|
||||
var pathString = path.Key.ToString();
|
||||
fileProperties.Add(pathString, new Dictionary<string, Dictionary<string, string?>> ());
|
||||
|
||||
var dict = new Dictionary<string, Dictionary<string, string>> ();
|
||||
foreach (var operation in operations)
|
||||
{
|
||||
var method = operation.Key.ToString().ToLower();
|
||||
method = GetHttpMethod(method);
|
||||
|
||||
if (method == string.Empty)
|
||||
{
|
||||
Console.Error.WriteLine($"Unsupported HTTP method found: '{operation.Key}'");
|
||||
Environment.Exit(3);
|
||||
}
|
||||
|
||||
fileProperties[pathString].Add(method, new Dictionary<string, string?> ());
|
||||
|
||||
var parameters = operation.Value.Parameters;
|
||||
string parametersList = string.Empty;
|
||||
|
||||
for (int i = 0; i < parameters.Count; i++)
|
||||
{
|
||||
var parameter = parameters[i];
|
||||
if (parameter.Schema.Type.ToLower() == "array")
|
||||
{
|
||||
parametersList += GetArrayKeyword(parameter.Schema) + " " + parameter.Name;
|
||||
}
|
||||
else if (parameter.Schema.Type.ToLower() == "object")
|
||||
{
|
||||
parametersList += parameter.Schema.Reference?.Id + $" user{parameter.Schema.Reference?.Id}";
|
||||
}
|
||||
else
|
||||
{
|
||||
parametersList += GetDataTypeKeyword(parameter.Schema) + " " + parameter.Name;
|
||||
}
|
||||
|
||||
if (i < parameters.Count - 1)
|
||||
{
|
||||
parametersList += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
fileProperties[pathString][method].Add("parameters", parametersList);
|
||||
|
||||
var responses = operation.Value.Responses;
|
||||
|
||||
foreach (var response in responses)
|
||||
{
|
||||
string returnValue;
|
||||
|
||||
// some responses doesn't have "content" property
|
||||
// so these would later return a default value
|
||||
if (response.Value.Content == null || response.Value.Content.Count == 0)
|
||||
{
|
||||
fileProperties[pathString][method].Add(response.Key, null);
|
||||
continue;
|
||||
}
|
||||
var content = response.Value.Content.First().Value;
|
||||
var schema = content.Schema;
|
||||
|
||||
if (schema == null)
|
||||
{
|
||||
Console.Error.WriteLine("No schema was found for the response.");
|
||||
Environment.Exit(3);
|
||||
}
|
||||
|
||||
if (schema.Type.ToLower() == "array")
|
||||
{
|
||||
returnValue = "new " + GetArrayKeyword(schema) + " {}";
|
||||
}
|
||||
else if (schema.Type.ToLower() == "object")
|
||||
{
|
||||
returnValue = "new " + schema?.Reference?.Id + "()";
|
||||
}
|
||||
else
|
||||
{
|
||||
returnValue = GetPrimitiveValue(schema);
|
||||
}
|
||||
|
||||
// this code below is for parsing sample values
|
||||
// this is used for demoing the project
|
||||
if (content.Example != null)
|
||||
{
|
||||
returnValue = GetSampleValue(content.Example, schema);
|
||||
}
|
||||
|
||||
if (content.Examples != null && content.Examples.Count > 0)
|
||||
{
|
||||
returnValue = GetSampleValue(content.Examples.First().Value.Value, schema);
|
||||
}
|
||||
|
||||
fileProperties[pathString][method].Add(response.Key, returnValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var schemas = document?.Components?.Schemas;
|
||||
|
||||
Dictionary<string, Dictionary<string, string>> schemaDict = new Dictionary<string, Dictionary<string, string>> ();
|
||||
if (schemas != null && schemas.Count > 0)
|
||||
{
|
||||
foreach (var schema in schemas)
|
||||
{
|
||||
schemaDict.Add(schema.Key, new Dictionary<string, string>());
|
||||
foreach (var property in schema.Value.Properties)
|
||||
{
|
||||
string propertyType;
|
||||
if (property.Value.Type.ToLower() == "array")
|
||||
{
|
||||
propertyType = GetArrayKeyword(property.Value);
|
||||
}
|
||||
else if (property.Value.Reference?.Id != null)
|
||||
{
|
||||
propertyType = property.Value.Reference.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
propertyType = GetDataTypeKeyword(property.Value);
|
||||
}
|
||||
schemaDict[schema.Key].Add(property.Key, propertyType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var page = new MinimalApiTemplate
|
||||
{
|
||||
FileProperties = fileProperties,
|
||||
Schemas = schemaDict
|
||||
};
|
||||
|
||||
var pageContent = page.TransformText();
|
||||
File.WriteAllText(args[1], pageContent);
|
||||
}
|
||||
|
||||
private static string GetSampleValue(IOpenApiAny example, OpenApiSchema? schema) => example switch
|
||||
{
|
||||
OpenApiString castedExample => $"\"{castedExample.Value}\"",
|
||||
OpenApiInteger castedExample => castedExample.Value.ToString(),
|
||||
OpenApiBoolean castedExample => castedExample.Value.ToString(),
|
||||
OpenApiFloat castedExample => castedExample.Value.ToString(),
|
||||
OpenApiDouble castedExample => castedExample.Value.ToString(),
|
||||
OpenApiArray castedExample => "new " + GetDataTypeKeyword(schema) + $"[] {{{GetArrayValues(castedExample)}}}",
|
||||
OpenApiObject castedExample => GetObjectArguments(castedExample, schema),
|
||||
OpenApiNull castedExample => "null",
|
||||
_ => string.Empty
|
||||
};
|
||||
|
||||
private static string GetArrayValues(OpenApiArray example)
|
||||
{
|
||||
int count = example.Count;
|
||||
string returnValue = string.Empty;
|
||||
foreach (var value in example)
|
||||
{
|
||||
returnValue += GetSampleValue(value,null);
|
||||
if (count > 1)
|
||||
{
|
||||
returnValue += ", ";
|
||||
}
|
||||
count--;
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
private static string GetObjectArguments(OpenApiObject example, OpenApiSchema? schema)
|
||||
{
|
||||
string arguments = $"new {schema?.Reference?.Id}(";
|
||||
for (int i = 0; i < example.Values.Count; i++)
|
||||
{
|
||||
if (schema?.Properties?.Values.Count > i)
|
||||
{
|
||||
arguments += $"{GetSampleValue(example.Values.ElementAt(i), schema?.Properties?.Values?.ElementAt(i))}, ";
|
||||
}
|
||||
else
|
||||
{
|
||||
arguments += $"{GetSampleValue(example.Values.ElementAt(i), null)}, ";
|
||||
}
|
||||
}
|
||||
return arguments.Substring(0, arguments.Length - 2) + ")";
|
||||
}
|
||||
|
||||
private static string GetHttpMethod(string method) => method switch
|
||||
{
|
||||
"get" => "MapGet",
|
||||
"post" => "MapPost",
|
||||
"put" => "MapPut",
|
||||
"delete" => "MapDelete",
|
||||
_ => string.Empty
|
||||
};
|
||||
|
||||
private static string GetDataTypeKeyword(OpenApiSchema? schema) => schema?.Type switch
|
||||
{
|
||||
"string" => "string",
|
||||
"integer" => "int",
|
||||
"float" => "float",
|
||||
"boolean" => "bool",
|
||||
"double" => "double",
|
||||
_ => string.Empty
|
||||
};
|
||||
|
||||
private static string GetArrayKeyword(OpenApiSchema? schema)
|
||||
{
|
||||
if (schema == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
string returnValue = "[";
|
||||
while (schema.Items.Type == "array")
|
||||
{
|
||||
returnValue += ",";
|
||||
schema = schema.Items;
|
||||
}
|
||||
if (schema.Items.Type.ToLower() == "object")
|
||||
{
|
||||
returnValue = schema?.Items.Reference?.Id + returnValue + "]";
|
||||
}
|
||||
else
|
||||
{
|
||||
returnValue = GetDataTypeKeyword(schema.Items) + returnValue + "]";
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
private static string GetPrimitiveValue(OpenApiSchema? schema) => schema?.Type switch
|
||||
{
|
||||
"string" => "\"\"",
|
||||
"integer" => "0",
|
||||
"boolean" => "false",
|
||||
"float" => "0.0f",
|
||||
"double" => "0.0d",
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
private static OpenApiDocument? ReadJson(string args)
|
||||
{
|
||||
if (!Path.IsPathRooted(args))
|
||||
{
|
||||
Console.Error.WriteLine("The file path you entered does not have a root");
|
||||
return null;
|
||||
}
|
||||
|
||||
OpenApiStreamReader reader = new OpenApiStreamReader();
|
||||
var diagnostic = new OpenApiDiagnostic();
|
||||
|
||||
try
|
||||
{
|
||||
string path = Path.GetFullPath(args);
|
||||
Stream stream = File.OpenRead(path);
|
||||
OpenApiDocument newDocument = reader.Read(stream, out diagnostic);
|
||||
return newDocument;
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
{
|
||||
Console.WriteLine("Check to make sure you entered a correct file path because the file was not found.");
|
||||
Console.Error.WriteLine(e.Message);
|
||||
Environment.Exit(1);
|
||||
return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("Check the file path you entered for errors.");
|
||||
Console.Error.WriteLine(e.Message);
|
||||
Environment.Exit(1);
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (diagnostic.Errors.Count == 0)
|
||||
{
|
||||
Console.WriteLine("Read File Successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var error in diagnostic.Errors)
|
||||
{
|
||||
Console.WriteLine($"There was an error reading in the file: {error.Pointer}");
|
||||
Console.Error.WriteLine(error.Message);
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
|
@ -7,9 +7,30 @@
|
|||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="MinimalApiTemplate.tt">
|
||||
<Generator>TextTemplatingFilePreprocessor</Generator>
|
||||
<LastGenOutput>MinimalApiTemplate.cs</LastGenOutput>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.OpenApi" Version="1.3.2" />
|
||||
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.3.2" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.TextTemplating.14.0" Version="14.3.25407" />
|
||||
<PackageReference Include="System.CodeDom" Version="6.0.2-mauipre.1.22102.15" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="MinimalApiTemplate.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>MinimalApiTemplate.tt</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
<#@ template language="C#" #>
|
||||
<#@ assembly name="mscorlib" #>
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
var app = builder.Build();
|
||||
|
||||
<#
|
||||
foreach (var path in FileProperties) {
|
||||
var pathValue = path.Key;
|
||||
foreach (var operation in FileProperties[pathValue]) {
|
||||
var method = operation.Key;
|
||||
|
||||
var parameterList = operation.Value["parameters"];
|
||||
|
||||
if (parameterList != String.Empty) {
|
||||
parameterList = ", " + parameterList;
|
||||
}
|
||||
#>
|
||||
app.<#=method #>("<#=pathValue #>", (HttpContext context<#=parameterList #>) =>
|
||||
{
|
||||
<#
|
||||
foreach (var response in operation.Value) {
|
||||
string statusCode;
|
||||
string returnValue;
|
||||
if (response.Key != "parameters") {
|
||||
statusCode = response.Key;
|
||||
if (response.Value == null) {
|
||||
var statusMethod = statusCode switch {
|
||||
"202" => "Accepted()",
|
||||
"400" => "BadRequest()",
|
||||
"409" => "Conflict()",
|
||||
"204" => "NoContent()",
|
||||
"404" => "NotFound()",
|
||||
"200" => "Ok()",
|
||||
"401" => "Unauthorized()",
|
||||
"422" => "UnprocessableEntity()",
|
||||
_ => $"StatusCode({response.Key})"
|
||||
};
|
||||
returnValue = $"Results.{statusMethod}";
|
||||
}
|
||||
else {
|
||||
var statusMethod = statusCode switch {
|
||||
"202" => $"Accepted(_, {response.Value})",
|
||||
"400" => $"BadRequest({response.Value})",
|
||||
"409" => $"Conflict({response.Value})",
|
||||
"204" => "NoContent()",
|
||||
"404" => $"NotFound({response.Value})",
|
||||
"200" => $"Ok({response.Value})",
|
||||
"401" => "Unauthorized()",
|
||||
"422" => $"UnprocessableEntity({response.Value})",
|
||||
_ => $"StatusCode({response.Key})"
|
||||
};
|
||||
returnValue = $"Results.{statusMethod}";
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
#>
|
||||
if (context.Request.Headers["AcceptStatusCode"] == "<#=statusCode #>")
|
||||
{
|
||||
return <#=returnValue #>;
|
||||
}
|
||||
|
||||
<#
|
||||
}
|
||||
#>
|
||||
return null;
|
||||
});
|
||||
|
||||
<#
|
||||
}
|
||||
}
|
||||
#>
|
||||
|
||||
<#
|
||||
foreach (var schema in Schemas) {
|
||||
var customObject = schema.Key;
|
||||
#>
|
||||
public class <#=customObject #> {
|
||||
<#
|
||||
string constructorParameters = string.Empty;
|
||||
string constructorBody = string.Empty;
|
||||
foreach (var property in schema.Value) {
|
||||
var propertyName = property.Key;
|
||||
var propertyType = property.Value;
|
||||
constructorParameters += propertyType + "? " + propertyName + ", ";
|
||||
constructorBody += $"this.{propertyName} = {propertyName}\n";
|
||||
#>
|
||||
public <#=propertyType #>? <#=propertyName #> { get; set; }
|
||||
<#
|
||||
}
|
||||
constructorParameters = constructorParameters.Substring(0, constructorParameters.Length - 2);
|
||||
constructorBody = constructorBody.Substring(0, constructorBody.Length - 1);
|
||||
#>
|
||||
public <#=customObject #>(<#=constructorParameters #>) {
|
||||
<#
|
||||
var statements = constructorBody.Split("\n");
|
||||
foreach (var statement in statements) {
|
||||
#>
|
||||
<#=statement #>;
|
||||
<#
|
||||
}
|
||||
#>
|
||||
}
|
||||
public <#=customObject #>() {}
|
||||
}
|
||||
<#
|
||||
}
|
||||
#>
|
||||
|
||||
<#+
|
||||
#nullable enable
|
||||
public Dictionary<string, Dictionary<string, Dictionary<string, string?>>>? FileProperties { get; set; }
|
||||
#nullable disable
|
||||
public Dictionary<string, Dictionary<string, string>> Schemas { get; set; }
|
||||
#>
|
|
@ -1,57 +0,0 @@
|
|||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi.Readers;
|
||||
|
||||
public class App
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
new App().ReadJson(args);
|
||||
}
|
||||
|
||||
private void ReadJson(string[] args)
|
||||
{
|
||||
string inputPath = args[0];
|
||||
|
||||
if (!Path.IsPathRooted(inputPath))
|
||||
{
|
||||
Console.WriteLine("The file path you entered does not have a root");
|
||||
return;
|
||||
}
|
||||
|
||||
OpenApiStreamReader reader = new OpenApiStreamReader();
|
||||
var diagnostic = new OpenApiDiagnostic();
|
||||
|
||||
try
|
||||
{
|
||||
string path = Path.GetFullPath(inputPath);
|
||||
Stream stream = File.OpenRead(path);
|
||||
OpenApiDocument newDocument = reader.Read(stream, out diagnostic);
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
{
|
||||
Console.WriteLine("Check to make sure you entered a correct file path because the file was not found.");
|
||||
Console.Error.WriteLine(e.Message);
|
||||
return;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Console.WriteLine("Check the file path you entered for errors.");
|
||||
Console.Error.WriteLine(e.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (diagnostic.Errors.Count == 0)
|
||||
{
|
||||
Console.WriteLine("Read File Successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (OpenApiError error in diagnostic.Errors)
|
||||
{
|
||||
Console.WriteLine($"There was an error reading in the file at {error.Pointer}");
|
||||
Console.Error.WriteLine(error.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
# Code Generator doc
|
||||
|
||||
Created: August 9, 2022 10:23 AM
|
||||
Last Edited Time: August 10, 2022 10:30 AM
|
||||
|
||||
# Overview 🖥️
|
||||
|
||||
This is a CLI tool that generates the server side API route handler code in Minimal API from an Open API spec.
|
||||
|
||||
Some possible use cases for this tool are testing and validating API contracts, including mocking for a single service and performing UI integration tests with the generated mock server. This tool can also be used to facilitate decoupled development.
|
||||
|
||||
# How to use the tool 🔨
|
||||
|
||||
In order for the tool to work, there are a couple of steps we must take to properly set up the environment.
|
||||
|
||||
We must create the `MinimalApiTemplate.cs` file which will be created in design time by the `[MinimalApiTemplate.tt](http://MinimalApiTemplate.tt)` file. In order to do this:
|
||||
|
||||
1. Open the `OpenApi.sln` file on VS.
|
||||
2. Right click on the `[MinimalApiTemplate.tt](http://MinimalApiTemplate.tt)` and select `Properties` .
|
||||
3. Change the `Build Action` property to `Content` and the `Custom Tool` property to `TextTemplatingFilePreprocessor` .
|
||||
4. After doing this you should be able to right click the `[MinimalApiTemplate.tt](http://MinimalApiTemplate.tt)` file and select `Run Custom Tool` .
|
||||
5. This will generate the `MinimalApiTemplate.cs` file which will be responsible for writing the code to the output file.
|
||||
|
||||
At this point, the tool is ready for use, and can be executed with the following command:
|
||||
|
||||
`dotnet run <open_api_spec_path> <output_file_path>`
|
||||
|
||||
### Arguments
|
||||
|
||||
`<open_api_spec_path>` corresponds to the file path of the json file containing the Open API documentation.
|
||||
|
||||
`<output_file_path>` is the path where the output file will be created.
|
||||
|
||||
## Constraints
|
||||
|
||||
`<open_api_spec_path>` has to be a valid path to .json file.
|
||||
|
||||
`<output_file_path>` has to be a valid path to .cs file. If a file path for an existing file is passed, the existing file will be overwritten.
|
||||
|
||||
# Implementation ⚙️
|
||||
|
||||
## Logic
|
||||
|
||||
Inside of the `CodeGenerator.cs` file, the `OpenApi.Reader` package is used to read the Open Api spec into an `OpenApiDocument` .
|
||||
|
||||
Then , parse the properties of `OpenApiDocument` (paths, HTTP methods, arguments, schemas, etc) into two dictionaries- `fileProperties` and `schemaDict` .
|
||||
|
||||
Both of these dictionaries are passed onto `[MinimalApiTemplate.tt](http://MinimalApiTemplate.tt)` , which is the T4 template with the code generation pattern.
|
||||
|
||||
The T4 template iterates through the dictionaries and uses the values to write the code pattern for the route handlers. The `MinimalApiTemplate.cs` file generated by
|
||||
|
||||
the T4 template during design time contains the method `TranformText()` that is used to produce the output code as a string value.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Microsoft.OpenApi (1.3.2)
|
||||
- Microsoft.OpenApi.Reader (1.3.2)
|
||||
- System.CodeDom (6.0.0)
|
||||
- Microsoft.VisualStudio.TextTemplating.15.0
|
||||
|
||||
# Sample
|
||||
|
||||
## Input
|
||||
|
||||
```json
|
||||
"openapi": "3.0.2",
|
||||
"info": {
|
||||
"title": "Swagger Petstore - OpenAPI 3.0",
|
||||
"description": "This is a sample server",
|
||||
"version": "1.0.11"
|
||||
},
|
||||
"externalDocs": {
|
||||
"description": "Find out more about Swagger",
|
||||
"url": "http://swagger.io"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "/api/v3"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"name": "pet",
|
||||
"description": "Everything about your Pets",
|
||||
"externalDocs": {
|
||||
"description": "Find out more",
|
||||
"url": "http://swagger.io"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "store",
|
||||
"description": "Access to Petstore orders",
|
||||
"externalDocs": {
|
||||
"description": "Find out more about our store",
|
||||
"url": "http://swagger.io"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "user",
|
||||
"description": "Operations about user"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"store"
|
||||
],
|
||||
"summary": "Welcome message",
|
||||
"description": "Write the welcome message",
|
||||
"operationId": "welcomeUser",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful operation",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "Welcome to the store!"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/pet": {
|
||||
"put": {
|
||||
"tags": [
|
||||
"pet"
|
||||
],
|
||||
"summary": "Update an existing pet",
|
||||
"description": "Update an existing pet by Id",
|
||||
"operationId": "updatePet",
|
||||
"requestBody": {
|
||||
"description": "Update an existent pet in the store",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful operation",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
},
|
||||
"example": {
|
||||
"id": 56,
|
||||
"name": "Max",
|
||||
"category": {
|
||||
"id": 56,
|
||||
"name": "Max"
|
||||
},
|
||||
"photoUrls": [
|
||||
"http://samplelink.com/image1",
|
||||
"http://samplelink.com/image2"
|
||||
],
|
||||
"tags": null,
|
||||
"status": "available"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid ID supplied"
|
||||
},
|
||||
"404": {
|
||||
"description": "Pet not found"
|
||||
},
|
||||
"405": {
|
||||
"description": "Validation exception"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"petstore_auth": [
|
||||
"write:pets",
|
||||
"read:pets"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
```csharp
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
var app = builder.Build();
|
||||
|
||||
app.MapGet("/", (HttpContext context) =>
|
||||
{
|
||||
if (context.Request.Headers["AcceptStatusCode"] == "200")
|
||||
{
|
||||
return Results.Ok("Welcome to the store!");
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
app.MapPut("/pet", (HttpContext context) =>
|
||||
{
|
||||
if (context.Request.Headers["AcceptStatusCode"] == "200")
|
||||
{
|
||||
return Results.Ok(new Pet(56, "Max", new Category(56, "Max"), new [] {"http://samplelink.com/image1", "http://samplelink.com/image2"}, null, "available"));
|
||||
}
|
||||
|
||||
if (context.Request.Headers["AcceptStatusCode"] == "400")
|
||||
{
|
||||
return Results.BadRequest();
|
||||
}
|
||||
|
||||
if (context.Request.Headers["AcceptStatusCode"] == "404")
|
||||
{
|
||||
return Results.NotFound();
|
||||
}
|
||||
|
||||
if (context.Request.Headers["AcceptStatusCode"] == "405")
|
||||
{
|
||||
return Results.StatusCode(405);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
```
|
||||
|
||||
# Additional Notes 📒
|
Загрузка…
Ссылка в новой задаче