Refitter is a CLI tool for generating a C# REST API Client using the Refit library from OpenAPI specifications.
$ dotnet add package RefitterRefitter is a CLI tool for generating a C# REST API Client using the Refit library. Refitter can generate the Refit interface from OpenAPI specifications
The tool is packaged as a .NET Tool and is published to nuget.org. You can install the latest version of this tool like this:
dotnet tool install --global Refitter
$ refitter --help
USAGE:
refitter [URL or input file] [OPTIONS]
EXAMPLES:
refitter ./openapi.json
refitter https://petstore3.swagger.io/api/v3/openapi.yaml
refitter ./openapi.json --settings-file ./openapi.refitter --output ./GeneratedCode.cs
refitter ./openapi.json --namespace "Your.Namespace.Of.Choice.GeneratedCode" --output ./GeneratedCode.cs
refitter ./openapi.json --namespace "Your.Namespace.Of.Choice.GeneratedCode" --internal
refitter ./openapi.json --output ./IGeneratedCode.cs --interface-only
refitter ./openapi.json --use-api-response
refitter ./openapi.json --cancellation-tokens
refitter ./openapi.json --no-operation-headers
refitter ./openapi.json --no-accept-headers
refitter ./openapi.json --use-iso-date-format
refitter ./openapi.json --additional-namespace "Your.Additional.Namespace" --additional-namespace "Your.Other.Additional.Namespace"
refitter ./openapi.json --multiple-interfaces ByEndpoint
refitter ./openapi.json --tag Pet --tag Store --tag User
refitter ./openapi.json --match-path '^/pet/.*'
refitter ./openapi.json --trim-unused-schema
refitter ./openapi.json --trim-unused-schema --keep-schema '^Model$' --keep-schema '^Person.+'
refitter ./openapi.json --no-deprecated-operations
refitter ./openapi.json --operation-name-template '{operationName}Async'
refitter ./openapi.json --optional-nullable-parameters
ARGUMENTS:
[URL or input file] URL or file path to OpenAPI Specification file
OPTIONS:
DEFAULT
-h, --help Prints help information
-v, --version Prints version information
-s, --settings-file Path to .refitter settings file. Specifying this will ignore all other settings (except for --output)
-n, --namespace GeneratedCode Default namespace to use for generated types
-o, --output Output.cs Path to Output file
--no-auto-generated-header Don't add <auto-generated> header to output file
--no-accept-headers Don't add <Accept> header to output file
--interface-only Don't generate contract types
--use-api-response Return Task<IApiResponse<T>> instead of Task<T>
--use-observable-response Return IObservable instead of Task
--internal Set the accessibility of the generated types to 'internal'
--cancellation-tokens Use cancellation tokens
--no-operation-headers Don't generate operation headers
--no-logging Don't log errors or collect telemetry
--additional-namespace Add additional namespace to generated types
--use-iso-date-format Explicitly format date query string parameters in ISO 8601 standard date format using delimiters (2023-06-15)
--multiple-interfaces Generate a Refit interface for each endpoint. May be one of ByEndpoint, ByTag
--match-path Only include Paths that match the provided regular expression. May be set multiple times
--tag Only include Endpoints that contain this tag. May be set multiple times and result in OR'ed evaluation
--skip-validation Skip validation of the OpenAPI specification
--no-deprecated-operations Don't generate deprecated operations
--operation-name-template Generate operation names using pattern. When using --multiple-interfaces ByEndpoint, this is name of the Execute() method in the interface
--optional-nullable-parameters Generate nullable parameters as optional parameters
--trim-unused-schema Removes unreferenced components schema to keep the generated output to a minimum
--keep-schema Force to keep matching schema, uses regular expressions. Use together with "--trim-unused-schema". Can be set multiple times
--no-banner Don't show donation banner
--skip-default-additional-properties Set to true to skip default additional properties
--operation-name-generator Default The NSwag IOperationNameGenerator implementation to use.
May be one of:
- Default
- MultipleClientsFromOperationId
- MultipleClientsFromPathSegments
- MultipleClientsFromFirstTagAndOperationId
- MultipleClientsFromFirstTagAndOperationName
- MultipleClientsFromFirstTagAndPathSegments
- SingleClientFromOperationId
- SingleClientFromPathSegments
See https://refitter.github.io/api/Refitter.Core.OperationNameGeneratorTypes.html for more information
The following is an example .refitter file
{
"openApiPath": "/path/to/your/openAPI", // Required
"namespace": "Org.System.Service.Api.GeneratedCode", // Optional. Default=GeneratedCode
"naming": {
"useOpenApiTitle": false, // Optional. Default=true
"interfaceName": "MyApiClient" // Optional. Default=ApiClient
},
"generateContracts": true, // Optional. Default=true
"generateXmlDocCodeComments": true, // Optional. Default=true
"generateStatusCodeComments": true, // Optional. Default=true
"addAutoGeneratedHeader": true, // Optional. Default=true
"addAcceptHeaders": true, // Optional. Default=true
"returnIApiResponse": false, // Optional. Default=false
"responseTypeOverride": { // Optional. Default={}
"File_Upload": "IApiResponse",
"File_Download": "System.Net.Http.HttpContent"
},
"generateOperationHeaders": true, // Optional. Default=true
"typeAccessibility": "Public", // Optional. Values=Public|Internal. Default=Public
"useCancellationTokens": false, // Optional. Default=false
"useIsoDateFormat": false, // Optional. Default=false
"multipleInterfaces": "ByEndpoint", // Optional. May be one of "ByEndpoint" or "ByTag"
"generateDeprecatedOperations": false, // Optional. Default=true
"operationNameTemplate": "{operationName}Async", // Optional. Must contain {operationName} when multipleInterfaces != ByEndpoint
"optionalParameters": false, // Optional. Default=false
"outputFolder": "../CustomOutput" // Optional. Default=./Generated
"outputFilename": "RefitInterface.cs", // Optional. Default=Output.cs for CLI tool
"additionalNamespaces": [ // Optional
"Namespace1",
"Namespace2"
],
"excludeNamespaces": [ // Optional. Exclude namespaces that match the provided regular expressions
"^Namespace[.].*",
"^Namespace$"
],
"includeTags": [ // Optional. OpenAPI Tag to include when generating code
"Pet",
"Store",
"User"
],
"includePathMatches": [ // Optional. Only include Paths that match the provided regular expression
"^/pet/.*",
"^/store/.*"
],
"trimUnusedSchema": false, // Optional. Default=false
"keepSchemaPatterns": [ // Optional. Force to keep matching schema, uses regular expressions. Use together with trimUnusedSchema=true
"^Model$",
"^Person.+"
],
"generateDefaultAdditionalProperties": true, // Optional. default=true
"operationNameGenerator": "Default", // Optional. May be one of Default, MultipleClientsFromOperationId, MultipleClientsFromPathSegments, MultipleClientsFromFirstTagAndOperationId, MultipleClientsFromFirstTagAndOperationName, MultipleClientsFromFirstTagAndPathSegments, SingleClientFromOperationId, SingleClientFromPathSegments
"dependencyInjectionSettings": { // Optional
"baseUrl": "https://petstore3.swagger.io/api/v3", // Optional. Leave this blank to set the base address manually
"httpMessageHandlers": [ // Optional
"AuthorizationMessageHandler",
"TelemetryMessageHandler"
],
"usePolly": true, // Optional. Set this to true, to configure Polly with a retry policy that uses a jittered backoff. Default=false
"pollyMaxRetryCount": 3, // Optional. Default=6
"firstBackoffRetryInSeconds": 0.5 // Optional. Default=1.0
},
"codeGeneratorSettings": { // Optional. Default settings are the values set in this example
"requiredPropertiesMustBeDefined": true,
"generateDataAnnotations": true,
"anyType": "object",
"dateType": "System.DateTimeOffset",
"dateTimeType": "System.DateTimeOffset",
"timeType": "System.TimeSpan",
"timeSpanType": "System.TimeSpan",
"arrayType": "System.Collections.Generic.ICollection",
"dictionaryType": "System.Collections.Generic.IDictionary",
"arrayInstanceType": "System.Collections.ObjectModel.Collection",
"dictionaryInstanceType": "System.Collections.Generic.Dictionary",
"arrayBaseType": "System.Collections.ObjectModel.Collection",
"dictionaryBaseType": "System.Collections.Generic.Dictionary",
"propertySetterAccessModifier": "",
"generateImmutableArrayProperties": false,
"generateImmutableDictionaryProperties": false,
"handleReferences": false,
"jsonSerializerSettingsTransformationMethod": null,
"generateJsonMethods": false,
"enforceFlagEnums": false,
"inlineNamedDictionaries": false,
"inlineNamedTuples": true,
"inlineNamedArrays": false,
"generateOptionalPropertiesAsNullable": false,
"generateNullableReferenceTypes": false,
"generateNativeRecords": false,
"generateDefaultValues": true,
"inlineNamedAny": false,
"excludedTypeNames": [
"ExcludedTypeFoo",
"ExcludedTypeBar"
]
}
}openApiPath - points to the OpenAPI Specifications file. This can be the path to a file stored on disk, relative to the .refitter file. This can also be a URL to a remote file that will be downloaded over HTTP/HTTPSnamespace - the namespace used in the generated code. If not specified, this defaults to GeneratedCodenaming.useOpenApiTitle - a boolean indicating whether the OpenApi title should be used. Default is truenaming.interfaceName - the name of the generated interface. The generated code will automatically prefix this with I so if this set to MyApiClient then the generated interface is called IMyApiClient. Default is ApiClientgenerateContracts - a boolean indicating whether contracts should be generated. A use case for this is several API clients use the same contracts. Default is truegenerateXmlDocCodeComments - a boolean indicating whether XML doc comments should be generated. Default is trueaddAutoGeneratedHeader - a boolean indicating whether XML doc comments should be generated. Default is trueaddAcceptHeaders - a boolean indicating whether to add accept headers [Headers("Accept: application/json")]. Default is truereturnIApiResponse - a boolean indicating whether to return IApiResponse<T> objects. Default is falseresponseTypeOverride - a dictionary with operation ids (as specified in the OpenAPI document) and a particular return type to use. The types are wrapped in a task, but otherwise unmodified (so make sure to specify or import their namespaces). Default is {}generateOperationHeaders - a boolean indicating whether to use operation headers in the generated methods. Default is truetypeAccessibility - the generated type accessibility. Possible values are Public and Internal. Default is PublicuseCancellationTokens - Use cancellation tokens in the generated methods. Default is falseuseIsoDateFormat - Set to true to explicitly format date query string parameters in ISO 8601 standard date format using delimiters (for example: 2023-06-15). Default is falsemultipleInterfaces - Set to ByEndpoint to generate an interface for each endpoint, or ByTag to group Endpoints by their Tag (like SwaggerUI groups them).outputFolder - a string describing a relative path to a desired output folder. Default is ./GeneratedoutputFilename - Output filename. Default is Output.cs when used from the CLI tool, otherwise its the .refitter filename. So Petstore.refitter becomes Petstore.cs.additionalNamespaces - A collection of additional namespaces to include in the generated file. A use case for this is when you want to reuse contracts from a different namespace than the generated code. Default is emptyexcludeNamespaces - A collection of regular expressions to exclude namespaces from the generated file. A use case for this is when your project has global usings where these namepsaces would be redundant. Default is emptyincludeTags - A collection of tags to use a filter for including endpoints that contain this tag.includePathMatches - A collection of regular expressions used to filter paths.generateDeprecatedOperations - a boolean indicating whether deprecated operations should be generated or skipped. Default is trueoperationNameTemplate - Generate operation names using pattern. This must contain the string {operationName}. An example usage of this could be {operationName}Async to suffix all method names with AsyncoptionalParameters - Generate non-required parameters as nullable optional parameterstrimUnusedSchema - Removes unreferenced components schema to keep the generated output to a minimumkeepSchemaPatterns: A collection of regular expressions to force to keep matching schema. This is used together with trimUnusedSchemagenerateDefaultAdditionalProperties: Set to false to skip default additional properties. Default is trueoperationNameGenerator: The NSwag IOperationNameGenerator implementation to use. See https://refitter.github.io/api/Refitter.Core.OperationNameGeneratorTypes.htmldependencyInjectionSettings - Setting this will generated extension methods to IServiceCollection for configuring Refit clients
baseUrl - Used as the HttpClient base address. Leave this blank to manually set the base URLhttpMessageHandlers - A collection of HttpMessageHandler that is added to the HttpClient pipelineusePolly - Set this to true to configure the HttpClient to use Polly using a retry policy with a jittered backoffpollyMaxRetryCount - This is the max retry count used in the Polly retry policy. Default is 6firstBackoffRetryInSeconds - This is the duration of the initial retry backoff. Default is 1 secondcodeGeneratorSettings - Setting this allows customization of the NSwag generated types and contracts
requiredPropertiesMustBeDefined - Default is true,generateDataAnnotations - Default is true,anyType - Default is object,dateType - Default is System.DateTimeOffset,dateTimeType - Default is System.DateTimeOffset,timeType - Default is System.TimeSpan,timeSpanType - Default is System.TimeSpan,arrayType - Default is System.Collections.Generic.ICollection,dictionaryType - Default is System.Collections.Generic.IDictionary,arrayInstanceType - Default is System.Collections.ObjectModel.Collection,dictionaryInstanceType - Default is System.Collections.Generic.Dictionary,arrayBaseType - Default is System.Collections.ObjectModel.Collection,dictionaryBaseType - Default is System.Collections.Generic.Dictionary,propertySetterAccessModifier - Default is ``,generateImmutableArrayProperties - Default is false,generateImmutableDictionaryProperties - Default is false,handleReferences - Default is false,jsonSerializerSettingsTransformationMethod - Default is null,generateJsonMethods - Default is false,enforceFlagEnums - Default is false,inlineNamedDictionaries - Default is false,inlineNamedTuples - Default is true,inlineNamedArrays - Default is false,generateOptionalPropertiesAsNullable - Default is false,generateNullableReferenceTypes - Default is false,To generate code from an OpenAPI specifications file, run the following:
$ refitter [path to OpenAPI spec file] --namespace "[Your.Namespace.Of.Choice.GeneratedCode]"This will generate a file called Output.cs which contains the Refit interface and contract classes generated using NSwag
Here's an example generated output from the Swagger Petstore example using the default settings
$ refitter ./openapi.json --namespace "Your.Namespace.Of.Choice.GeneratedCode"using Refit;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Your.Namespace.Of.Choice.GeneratedCode
{
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface ISwaggerPetstore
{
/// <summary>Update an existing pet</summary>
/// <remarks>Update an existing pet by Id</remarks>
/// <param name="body">Update an existent pet in the store</param>
/// <returns>Successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid ID supplied</description>
/// </item>
/// <item>
/// <term>404</term>
/// <description>Pet not found</description>
/// </item>
/// <item>
/// <term>405</term>
/// <description>Validation exception</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/xml, application/json")]
[Put("/pet")]
Task<Pet> UpdatePet([Body] Pet body);
/// <summary>Add a new pet to the store</summary>
/// <remarks>Add a new pet to the store</remarks>
/// <param name="body">Create a new pet in the store</param>
/// <returns>Successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>405</term>
/// <description>Invalid input</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/xml, application/json")]
[Post("/pet")]
Task<Pet> AddPet([Body] Pet body);
/// <summary>Finds Pets by status</summary>
/// <remarks>Multiple status values can be provided with comma separated strings</remarks>
/// <param name="status">Status values that need to be considered for filter</param>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid status value</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/json")]
[Get("/pet/findByStatus")]
Task<ICollection<Pet>> FindPetsByStatus([Query] Status? status);
/// <summary>Finds Pets by tags</summary>
/// <remarks>Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.</remarks>
/// <param name="tags">Tags to filter by</param>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid tag value</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/json")]
[Get("/pet/findByTags")]
Task<ICollection<Pet>> FindPetsByTags([Query(CollectionFormat.Multi)] IEnumerable<string> tags);
/// <summary>Find pet by ID</summary>
/// <remarks>Returns a single pet</remarks>
/// <param name="petId">ID of pet to return</param>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid ID supplied</description>
/// </item>
/// <item>
/// <term>404</term>
/// <description>Pet not found</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/xml, application/json")]
[Get("/pet/{petId}")]
Task<Pet> GetPetById(long petId);
/// <summary>Updates a pet in the store with form data</summary>
/// <param name="petId">ID of pet that needs to be updated</param>
/// <param name="name">Name of pet that needs to be updated</param>
/// <param name="status">Status of pet that needs to be updated</param>
/// <returns>A <see cref="Task"/> that completes when the request is finished.</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>405</term>
/// <description>Invalid input</description>
/// </item>
/// </list>
/// </exception>
[Post("/pet/{petId}")]
Task UpdatePetWithForm(long petId, [Query] string name, [Query] string status);
/// <summary>Deletes a pet</summary>
/// <param name="petId">Pet id to delete</param>
/// <returns>A <see cref="Task"/> that completes when the request is finished.</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid pet value</description>
/// </item>
/// </list>
/// </exception>
[Delete("/pet/{petId}")]
Task DeletePet(long petId, [Header("api_key")] string api_key);
/// <summary>uploads an image</summary>
/// <param name="petId">ID of pet to update</param>
/// <param name="additionalMetadata">Additional Metadata</param>
/// <returns>
/// A <see cref="Task"/> representing the <see cref="IApiResponse"/> instance containing the result:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>200</term>
/// <description>successful operation</description>
/// </item>
/// </list>
/// </returns>
[Headers("Accept: application/json")]
[Post("/pet/{petId}/uploadImage")]
Task<ApiResponse> UploadFile(long petId, [Query] string additionalMetadata, StreamPart body);
/// <summary>Returns pet inventories by status</summary>
/// <remarks>Returns a map of status codes to quantities</remarks>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
[Headers("Accept: application/json")]
[Get("/store/inventory")]
Task<IDictionary<string, int>> GetInventory();
/// <summary>Place an order for a pet</summary>
/// <remarks>Place a new order in the store</remarks>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>405</term>
/// <description>Invalid input</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/json")]
[Post("/store/order")]
Task<Order> PlaceOrder([Body] Order body);
/// <summary>Find purchase order by ID</summary>
/// <remarks>For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions</remarks>
/// <param name="orderId">ID of order that needs to be fetched</param>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid ID supplied</description>
/// </item>
/// <item>
/// <term>404</term>
/// <description>Order not found</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/json")]
[Get("/store/order/{orderId}")]
Task<Order> GetOrderById(long orderId);
/// <summary>Delete purchase order by ID</summary>
/// <remarks>For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors</remarks>
/// <param name="orderId">ID of the order that needs to be deleted</param>
/// <returns>A <see cref="Task"/> that completes when the request is finished.</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid ID supplied</description>
/// </item>
/// <item>
/// <term>404</term>
/// <description>Order not found</description>
/// </item>
/// </list>
/// </exception>
[Delete("/store/order/{orderId}")]
Task DeleteOrder(long orderId);
/// <summary>Create user</summary>
/// <remarks>This can only be done by the logged in user.</remarks>
/// <param name="body">Created user object</param>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
[Headers("Accept: application/json, application/xml")]
[Post("/user")]
Task CreateUser([Body] User body);
/// <summary>Creates list of users with given input array</summary>
/// <remarks>Creates list of users with given input array</remarks>
/// <returns>Successful operation</returns>
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
[Headers("Accept: application/xml, application/json")]
[Post("/user/createWithList")]
Task<User> CreateUsersWithListInput([Body] IEnumerable<User> body);
/// <summary>Logs user into the system</summary>
/// <param name="username">The user name for login</param>
/// <param name="password">The password for login in clear text</param>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid username/password supplied</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/json")]
[Get("/user/login")]
Task<string> LoginUser([Query] string username, [Query] string password);
/// <summary>Logs out current logged in user session</summary>
/// <returns>A <see cref="Task"/> that completes when the request is finished.</returns>
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
[Get("/user/logout")]
Task LogoutUser();
/// <summary>Get user by user name</summary>
/// <param name="username">The name that needs to be fetched. Use user1 for testing.</param>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid username supplied</description>
/// </item>
/// <item>
/// <term>404</term>
/// <description>User not found</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/json")]
[Get("/user/{username}")]
Task<User> GetUserByName(string username);
/// <summary>Update user</summary>
/// <remarks>This can only be done by the logged in user.</remarks>
/// <param name="username">name that need to be deleted</param>
/// <param name="body">Update an existent user in the store</param>
/// <returns>A <see cref="Task"/> that completes when the request is finished.</returns>
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
[Put("/user/{username}")]
Task UpdateUser(string username, [Body] User body);
/// <summary>Delete user</summary>
/// <remarks>This can only be done by the logged in user.</remarks>
/// <param name="username">The name that needs to be deleted</param>
/// <returns>A <see cref="Task"/> that completes when the request is finished.</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid username supplied</description>
/// </item>
/// <item>
/// <term>404</term>
/// <description>User not found</description>
/// </item>
/// </list>
/// </exception>
[Delete("/user/{username}")]
Task DeleteUser(string username);
}
}Here's an example generated output from the Swagger Petstore example configured to generate an interface for each endpoint
CLI Tool
$ refitter ./openapi.json --namespace "Your.Namespace.Of.Choice.GeneratedCode" --multiple-interfaces ByEndpointOutput
/// <summary>Update an existing pet</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IUpdatePetEndpoint
{
/// <summary>Update an existing pet</summary>
/// <remarks>Update an existing pet by Id</remarks>
/// <param name="body">Update an existent pet in the store</param>
/// <returns>Successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid ID supplied</description>
/// </item>
/// <item>
/// <term>404</term>
/// <description>Pet not found</description>
/// </item>
/// <item>
/// <term>405</term>
/// <description>Validation exception</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/xml, application/json")]
[Put("/pet")]
Task<Pet> Execute([Body] Pet body);
}
/// <summary>Add a new pet to the store</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IAddPetEndpoint
{
/// <summary>Add a new pet to the store</summary>
/// <remarks>Add a new pet to the store</remarks>
/// <param name="body">Create a new pet in the store</param>
/// <returns>Successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>405</term>
/// <description>Invalid input</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/xml, application/json")]
[Post("/pet")]
Task<Pet> Execute([Body] Pet body);
}
/// <summary>Finds Pets by status</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IFindPetsByStatusEndpoint
{
/// <summary>Finds Pets by status</summary>
/// <remarks>Multiple status values can be provided with comma separated strings</remarks>
/// <param name="status">Status values that need to be considered for filter</param>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid status value</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/json")]
[Get("/pet/findByStatus")]
Task<ICollection<Pet>> Execute([Query] Status? status);
}
/// <summary>Finds Pets by tags</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IFindPetsByTagsEndpoint
{
/// <summary>Finds Pets by tags</summary>
/// <remarks>Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.</remarks>
/// <param name="tags">Tags to filter by</param>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid tag value</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/json")]
[Get("/pet/findByTags")]
Task<ICollection<Pet>> Execute([Query(CollectionFormat.Multi)] IEnumerable<string> tags);
}
/// <summary>Find pet by ID</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IGetPetByIdEndpoint
{
/// <summary>Find pet by ID</summary>
/// <remarks>Returns a single pet</remarks>
/// <param name="petId">ID of pet to return</param>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid ID supplied</description>
/// </item>
/// <item>
/// <term>404</term>
/// <description>Pet not found</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/xml, application/json")]
[Get("/pet/{petId}")]
Task<Pet> Execute(long petId);
}
/// <summary>Updates a pet in the store with form data</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IUpdatePetWithFormEndpoint
{
/// <summary>Updates a pet in the store with form data</summary>
/// <param name="petId">ID of pet that needs to be updated</param>
/// <param name="name">Name of pet that needs to be updated</param>
/// <param name="status">Status of pet that needs to be updated</param>
/// <returns>A <see cref="Task"/> that completes when the request is finished.</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>405</term>
/// <description>Invalid input</description>
/// </item>
/// </list>
/// </exception>
[Post("/pet/{petId}")]
Task Execute(long petId, [Query] string name, [Query] string status);
}
/// <summary>Deletes a pet</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IDeletePetEndpoint
{
/// <summary>Deletes a pet</summary>
/// <param name="petId">Pet id to delete</param>
/// <returns>A <see cref="Task"/> that completes when the request is finished.</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid pet value</description>
/// </item>
/// </list>
/// </exception>
[Delete("/pet/{petId}")]
Task Execute(long petId, [Header("api_key")] string api_key);
}
/// <summary>uploads an image</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IUploadFileEndpoint
{
/// <summary>uploads an image</summary>
/// <param name="petId">ID of pet to update</param>
/// <param name="additionalMetadata">Additional Metadata</param>
/// <returns>
/// A <see cref="Task"/> representing the <see cref="IApiResponse"/> instance containing the result:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>200</term>
/// <description>successful operation</description>
/// </item>
/// </list>
/// </returns>
[Headers("Accept: application/json")]
[Post("/pet/{petId}/uploadImage")]
Task<ApiResponse> Execute(long petId, [Query] string additionalMetadata, StreamPart body);
}
/// <summary>Returns pet inventories by status</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IGetInventoryEndpoint
{
/// <summary>Returns pet inventories by status</summary>
/// <remarks>Returns a map of status codes to quantities</remarks>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
[Headers("Accept: application/json")]
[Get("/store/inventory")]
Task<IDictionary<string, int>> Execute();
}
/// <summary>Place an order for a pet</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IPlaceOrderEndpoint
{
/// <summary>Place an order for a pet</summary>
/// <remarks>Place a new order in the store</remarks>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>405</term>
/// <description>Invalid input</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/json")]
[Post("/store/order")]
Task<Order> Execute([Body] Order body);
}
/// <summary>Find purchase order by ID</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IGetOrderByIdEndpoint
{
/// <summary>Find purchase order by ID</summary>
/// <remarks>For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions</remarks>
/// <param name="orderId">ID of order that needs to be fetched</param>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid ID supplied</description>
/// </item>
/// <item>
/// <term>404</term>
/// <description>Order not found</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/json")]
[Get("/store/order/{orderId}")]
Task<Order> Execute(long orderId);
}
/// <summary>Delete purchase order by ID</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IDeleteOrderEndpoint
{
/// <summary>Delete purchase order by ID</summary>
/// <remarks>For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors</remarks>
/// <param name="orderId">ID of the order that needs to be deleted</param>
/// <returns>A <see cref="Task"/> that completes when the request is finished.</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid ID supplied</description>
/// </item>
/// <item>
/// <term>404</term>
/// <description>Order not found</description>
/// </item>
/// </list>
/// </exception>
[Delete("/store/order/{orderId}")]
Task Execute(long orderId);
}
/// <summary>Create user</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface ICreateUserEndpoint
{
/// <summary>Create user</summary>
/// <remarks>This can only be done by the logged in user.</remarks>
/// <param name="body">Created user object</param>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
[Headers("Accept: application/json, application/xml")]
[Post("/user")]
Task Execute([Body] User body);
}
/// <summary>Creates list of users with given input array</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface ICreateUsersWithListInputEndpoint
{
/// <summary>Creates list of users with given input array</summary>
/// <remarks>Creates list of users with given input array</remarks>
/// <returns>Successful operation</returns>
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
[Headers("Accept: application/xml, application/json")]
[Post("/user/createWithList")]
Task<User> Execute([Body] IEnumerable<User> body);
}
/// <summary>Logs user into the system</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface ILoginUserEndpoint
{
/// <summary>Logs user into the system</summary>
/// <param name="username">The user name for login</param>
/// <param name="password">The password for login in clear text</param>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid username/password supplied</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/json")]
[Get("/user/login")]
Task<string> Execute([Query] string username, [Query] string password);
}
/// <summary>Logs out current logged in user session</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface ILogoutUserEndpoint
{
/// <summary>Logs out current logged in user session</summary>
/// <returns>A <see cref="Task"/> that completes when the request is finished.</returns>
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
[Get("/user/logout")]
Task Execute();
}
/// <summary>Get user by user name</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IGetUserByNameEndpoint
{
/// <summary>Get user by user name</summary>
/// <param name="username">The name that needs to be fetched. Use user1 for testing.</param>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid username supplied</description>
/// </item>
/// <item>
/// <term>404</term>
/// <description>User not found</description>
/// </item>
/// </list>
/// </exception>
[Headers("Accept: application/json")]
[Get("/user/{username}")]
Task<User> Execute(string username);
}
/// <summary>Update user</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IUpdateUserEndpoint
{
/// <summary>Update user</summary>
/// <remarks>This can only be done by the logged in user.</remarks>
/// <param name="username">name that need to be deleted</param>
/// <param name="body">Update an existent user in the store</param>
/// <returns>A <see cref="Task"/> that completes when the request is finished.</returns>
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
[Put("/user/{username}")]
Task Execute(string username, [Body] User body);
}
/// <summary>Delete user</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IDeleteUserEndpoint
{
/// <summary>Delete user</summary>
/// <remarks>This can only be done by the logged in user.</remarks>
/// <param name="username">The name that needs to be deleted</param>
/// <returns>A <see cref="Task"/> that completes when the request is finished.</returns>
/// <exception cref="ApiException">
/// Thrown when the request returns a non-success status code:
/// <list type="table">
/// <listheader>
/// <term>Status</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>400</term>
/// <description>Invalid username supplied</description>
/// </item>
/// <item>
/// <term>404</term>
/// <description>User not found</description>
/// </item>
/// </list>
/// </exception>
[Delete("/user/{username}")]
Task Execute(string username);
}Here's an example usage of the generated code above
using Refit;
using System;
using System.Threading.Tasks;
namespace Your.Namespace.Of.Choice.GeneratedCode;
internal class Program
{
private static async Task Main(string[] args)
{
var client = RestService.For<ISwaggerPetstore>("https://petstore3.swagger.io/api/v3");
var pet = await client.GetPetById(1);
Console.WriteLine("## Using Task<T> as return type ##");
Console.WriteLine($"Name: {pet.Name}");
Console.WriteLine($"Category: {pet.Category.Name}");
Console.WriteLine($"Status: {pet.Status}");
Console.WriteLine();
var client2 = RestService.For<WithApiResponse.ISwaggerPetstore>("https://petstore3.swagger.io/api/v3");
var response = await client2.GetPetById(2);
Console.WriteLine("## Using Task<IApiResponse<T>> as return type ##");
Console.WriteLine($"HTTP Status Code: {response.StatusCode}");
Console.WriteLine($"Name: {response.Content.Name}");
Console.WriteLine($"Category: {response.Content.Category.Name}");
Console.WriteLine($"Status: {response.Content.Status}");
}
}The RestService class generates an implementation of ISwaggerPetstore that uses HttpClient to make its calls.
The code above when run will output something like this:
## Using Task<T> as return type ##
Name: Gatitotototo
Category: Chaucito
Status: Sold
## Using Task<IApiResponse<T>> as return type ##
HTTP Status Code: OK
Name: Gatitotototo
Category: Chaucito
Status: Sold
Here's an example Minimal API with the Refit.HttpClientFactory library:
using Refit;
using Your.Namespace.Of.Choice.GeneratedCode;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services
.AddRefitClient<ISwaggerPetstore>()
.ConfigureHttpClient(c => c.BaseAddress = new Uri("https://petstore3.swagger.io/api/v3"));
var app = builder.Build();
app.MapGet(
"/pet/{id:long}",
async (ISwaggerPetstore petstore, long id) =>
{
try
{
return Results.Ok(await petstore.GetPetById(id));
}
catch (Refit.ApiException e)
{
return Results.StatusCode((int)e.StatusCode);
}
})
.WithName("GetPetById")
.WithOpenApi();
app.UseHttpsRedirection();
app.UseSwaggerUI();
app.UseSwagger();
app.Run();.NET Core supports registering the generated ISwaggerPetstore interface via HttpClientFactory
The following request to the API above
$ curl -X 'GET' 'https://localhost:5001/pet/1' -H 'accept: application/json'Returns a response that looks something like this:
{
"id": 1,
"name": "Special_char_owner_!@#$^&()`.testing",
"photoUrls": [
"https://petstore3.swagger.io/resources/photos/623389095.jpg"
],
"tags": [],
"status": "Sold"
}.NET 8.0
For tips and tricks on software development, check out my blog
If you find this useful and feel a bit generous then feel free to buy me a coffee ☕
generateNativeRecordsgenerateDefaultValues - Default is trueinlineNamedAny - Default is falseexcludedTypeNames - Default is empty