Eternet.Client.Http.Generator 2.1.10

Prefix Reserved
dotnet add package Eternet.Client.Http.Generator --version 2.1.10
                    
NuGet\Install-Package Eternet.Client.Http.Generator -Version 2.1.10
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Eternet.Client.Http.Generator" Version="2.1.10">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Eternet.Client.Http.Generator" Version="2.1.10" />
                    
Directory.Packages.props
<PackageReference Include="Eternet.Client.Http.Generator">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Eternet.Client.Http.Generator --version 2.1.10
                    
#r "nuget: Eternet.Client.Http.Generator, 2.1.10"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Eternet.Client.Http.Generator@2.1.10
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Eternet.Client.Http.Generator&version=2.1.10
                    
Install as a Cake Addin
#tool nuget:?package=Eternet.Client.Http.Generator&version=2.1.10
                    
Install as a Cake Tool

Eternet.Client.Http.Generator

Generador incremental para crear handlers HTTP a partir de contratos Eternet.Mediator.

Para la guía actual de autoría de contratos que alimentan este generator, ver ../docs/contracts-and-transport-authoring.md y ../docs/endpoint-manifest-ecosystem.md.

Soporta el caso clásico de un solo HttpClient por contrato y, además, el escenario de API Gateway con:

  • múltiples transportes por cliente (Internal, Gateway)
  • ruta proyectada por namespace
  • override de verbo HTTP en gateway
  • selección dinámica en runtime sin ensuciar Mediator

Paquetes

En la app consumidora:

<ItemGroup>
  <PackageReference Include="Eternet.Mediator" Version="x.y.z" />
  <PackageReference Include="Eternet.Client.Http.Generator" Version="x.y.z" PrivateAssets="all" />
</ItemGroup>

Si el contrato publica metadata de gateway:

<ItemGroup>
  <PackageReference Include="Eternet.Models" Version="x.y.z" />
</ItemGroup>

Alternativa local en workspace:

<ItemGroup>
  <ProjectReference Include="..\..\..\..\Eternet.AspNetCore\src\Eternet.Mediator\Eternet.Client.Http.Generator\Eternet.Client.Http.Generator.csproj"
                    OutputItemType="Analyzer"
                    ReferenceOutputAssembly="false" />
</ItemGroup>

Estrategia de runtime

El package tiene dos modos de uso.

App cliente pura

Una app cliente que no expone controladores no debe referenciar Eternet.Mediator.Generator. Con:

<ItemGroup>
  <PackageReference Include="Eternet.Mediator" Version="x.y.z" />
  <PackageReference Include="Eternet.Client.Http.Generator" Version="x.y.z" PrivateAssets="all" />
</ItemGroup>

el generator HTTP emite el runtime liviano necesario para:

  • services.AddMediator()
  • Mediator / IMediator
  • descubrimiento y registro de IRequestHandler<,> locales
  • ejecución de IPipelineBehavior<,> locales
  • fallback al dispatch HTTP generado cuando no existe handler local

Este es el caso pensado para Blazor/WASM y clientes que sólo consumen contratos o ejecutan comandos internos livianos.

App mixta o server

Si el proyecto ya usa Eternet.Mediator.Generator para generar el runtime server, pipelines y controladores, el runtime liviano del cliente debe apagarse explícitamente:

[assembly: Mediator.MediatorClientOptionsAttribute(GenerateMediatorRuntime = false)]

Con esa opción:

  • Eternet.Client.Http.Generator sigue generando handlers HTTP y services.AddHttpClientHandlers()
  • services.AddHttpClientHandlers() también registra los IRequestHandler<,> generados para que el IMediator principal del host pueda despacharlos
  • Eternet.Mediator.Generator queda como único dueño de services.AddMediator() y del runtime compartido
  • se evita cualquier duplicación de Mediator, AddMediator(...) o tipos de options

GenerateMediatorRuntime no es una opción de DI tardía; es un switch de ownership leído antes del codegen:

  • true: la app cliente dueña del proceso también es dueña del runtime liviano
  • false: el package cliente queda transport-only y el host conserva AddMediator(...), IMediator y los request senders

Usar un atributo, y no una opción runtime de AddMediator(...), permite decidir ese ownership antes de generar código.

Si consumís el generator vía PackageReference y necesitás declarar ese opt-out en source, definí una vez MediatorClientOptionsAttribute en el proyecto consumidor. El generator detecta ese tipo y no vuelve a emitirlo:

using System;

[assembly: Mediator.MediatorClientOptionsAttribute(GenerateMediatorRuntime = false)]

namespace Mediator;

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
public sealed class MediatorClientOptionsAttribute : Attribute
{
    public bool GenerateMediatorRuntime { get; set; } = true;
}

Si además querés customizar namespace, lifetime o ExtensionMethodNamespace, sumá esas properties al atributo local.

Caso simple

El uso existente no cambia:

using Eternet.Mediator;
using Eternet.Netmap.Contracts.Nodes.Commands;

[GenerateHttpClient("Default")]
internal abstract class DefaultHttpClient
{
    internal abstract class AddNodeHandler : AddNode;
    internal abstract class UpdateNodeHandler : UpdateNode;
}

El generator sigue emitiendo:

  • services.AddDefaultHttpClient(...)
  • services.AddHttpClientHandlers()

Registro:

services.AddHttpClientHandlers();
services.AddDefaultHttpClient(
    "https://internal-service/",
    configure: client =>
    {
        client.DefaultRequestHeaders.Add("X-App", "Netmap");
    },
    configureBuilder: builder =>
    {
        builder.AddHttpMessageHandler<MyAuthHandler>();
    });

Consumo:

public sealed class NodesViewModel(IGetResponse<AddNode.Response> response)
{
    public ValueTask<AddNode.Response> CreateAsync(AddNode.Request request, CancellationToken ct)
        => response.Get(request, ct);
}

IGetResponse<TResponse> sigue usando IMediator. En el runtime liviano del cliente, IMediator intenta resolver primero un IRequestHandler<,> local y, si no existe, cae al handler HTTP generado para ese contrato.

Múltiples transportes

Cuando el mismo contrato puede consumirse por el servicio interno o por API Gateway, repetí GenerateHttpClient con distinto transporte:

using Eternet.Mediator;

[GenerateHttpClient("Default", Transport = HttpClientTransport.Internal)]
[GenerateHttpClient("Default", Transport = HttpClientTransport.Gateway)]
internal abstract class DefaultHttpClient
{
    internal abstract class GetProductHandler : GetProduct;
}

El generator produce:

  • services.AddDefaultHttpClient(...)
  • services.AddDefaultHttpClientInternal(...)
  • services.AddDefaultHttpClientGateway(...)

Registros típicos:

services.AddHttpClientHandlers();

services.AddDefaultHttpClientInternal(
    "https://internal-service/",
    configure: client =>
    {
        client.DefaultRequestHeaders.Add("X-Transport", "internal");
    },
    configureBuilder: builder =>
    {
        builder.AddHttpMessageHandler<InternalAuthHandler>();
        builder.AddPolicyHandler(InternalRetryPolicy.Create());
    });

services.AddDefaultHttpClientGateway(
    "https://api-gateway/",
    configure: client =>
    {
        client.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", "gateway-token");
    },
    configureBuilder: builder =>
    {
        builder.AddHttpMessageHandler<GatewayAuthHandler>();
    });

La sobrecarga configureBuilder existe para que el helper generado no limite la configuración del IHttpClientBuilder.

Proyecciones de API Gateway

ApiGatewayEndpointAttribute vive en Eternet.Models.Attributes y modela la proyección publicada por gateway:

using Eternet.Mediator;
using Eternet.Mediator.Abstractions.Handlers;
using Eternet.Mediator.Attributes.Mvc;
using Eternet.Models;
using Eternet.Models.Attributes;

[GenerateEndpoint("Products")]
public abstract class GetProduct
    : DomainResultHandlerAsync<GetProduct.Request, GetProduct.Response>
{
    public record Request : CreateEntityCommand<Response>
    {
        [FromRoute] public required int Id { get; init; }
    }

    public record Response : CreateEntityDomainResult
    {
        public string Name { get; init; } = string.Empty;
    }

    [HttpGet("Internal/{id}")]
    [ApiGatewayEndpoint(OperationNamespace.Customers, "Public/Products/{id}", HttpMethod = "POST")]
    [ApiGatewayEndpoint(OperationNamespace.Billing, "BillingProducts/{id}")]
    public abstract override ValueTask<Response> Handle(
        Request request,
        CancellationToken cancellationToken);
}

Soporte actual:

  • transporte Internal o Gateway
  • namespace publicado en gateway
  • route override
  • HTTP method override

No soporta todavía transformaciones arbitrarias del body ni rename de parámetros.

Selección en runtime

Para el path por defecto seguí usando IGetResponse<TResponse>:

public sealed class InternalProductsViewModel(IGetResponse<GetProduct.Response> response)
{
    public ValueTask<GetProduct.Response> LoadAsync(GetProduct.Request request, CancellationToken ct)
        => response.Get(request, ct);
}

Para elegir gateway en runtime usá IGetResponseFactory:

public sealed class GatewayProductsViewModel(
    IGetResponseFactory responseFactory)
{
    public ValueTask<GetProduct.Response> LoadCustomersAsync(
        GetProduct.Request request,
        CancellationToken ct)
    {
        return responseFactory
            .Gateway<GetProduct.Response>(OperationNamespace.Customers)
            .Get(request, ct);
    }

    public ValueTask<GetProduct.Response> LoadBillingAsync(
        GetProduct.Request request,
        CancellationToken ct)
    {
        return responseFactory
            .Gateway<GetProduct.Response>(OperationNamespace.Billing)
            .Get(request, ct);
    }

    public ValueTask<GetProduct.Response> LoadInternalAsync(
        GetProduct.Request request,
        CancellationToken ct)
    {
        return responseFactory
            .Internal<GetProduct.Response>()
            .Get(request, ct);
    }
}

Con eso:

  • IGetResponse<TResponse> queda para el flujo default
  • IGetResponseFactory permite elegir el transporte y el namespace de gateway en runtime
  • el handler generado resuelve HttpClient, ruta y verbo a partir del contexto y los atributos

Qué genera el package

Runtime liviano habilitado

Cuando GenerateMediatorRuntime queda en su valor por defecto (true), el package genera:

  • runtime liviano de Mediator
  • services.AddMediator()
  • registro de handlers locales IRequestHandler<,>
  • wrappers para ejecutar IPipelineBehavior<,>
  • handlers HTTP e infraestructura de IGetResponseFactory

La resolución default queda así:

  1. handler local IRequestHandler<,>
  2. handler HTTP generado

Runtime liviano deshabilitado

Cuando el proyecto define:

[assembly: Mediator.MediatorClientOptionsAttribute(GenerateMediatorRuntime = false)]

el package genera sólo la parte HTTP:

  • handlers HTTP
  • services.AddHttpClientHandlers()
  • registros IRequestHandler<,> para que el runtime principal del host pueda resolver esos contratos
  • IGetResponseFactory
  • GeneratedContextualGetResponse<T>

En ese modo no genera:

  • services.AddMediator()
  • Mediator
  • el runtime liviano que hace ownership de IMediator

En otras palabras: con GenerateMediatorRuntime = false, el cliente no intenta “ganar” IMediator; sólo aporta transporte. Si el host quiere inyectar IGetResponse<TResponse> en UI o servicios propios, debe registrar los request senders una sola vez desde su runtime compartido.

Transporte HTTP generado

Para contratos con múltiples transportes o proyecciones de gateway, el handler generado:

  • resuelve el HttpClient real a usar
  • proyecta la ruta final (Internal o Gateway/{namespace}/...)
  • proyecta el verbo final (GET, POST, etc.)
  • mantiene un único RequestClassHandlerWrapper<TRequest, TResponse>

Esto evita duplicar registros de Mediator para el mismo request.

Referencia rápida

  • GenerateHttpClientAttribute: nombre lógico del cliente y transporte
  • HttpClientTransport: Internal, Gateway
  • ApiGatewayEndpointAttribute: namespace, route y HttpMethod
  • MediatorClientOptionsAttribute.GenerateMediatorRuntime: switch de ownership del runtime (true = cliente puro, false = host/mixed app)
  • IGetResponse<TResponse>: consumo default vía IMediator
  • IGetResponseFactory: selección explícita de transporte en runtime

Ver también

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.1.10 0 4/23/2026
2.1.9 83 4/13/2026
2.1.8 93 4/13/2026
2.1.7 83 4/12/2026
2.1.6 81 4/12/2026
2.1.5 87 4/10/2026
2.1.4 112 4/3/2026
2.1.3 88 4/2/2026
2.1.2 100 3/31/2026
2.1.1 99 3/30/2026
2.1.0 81 3/30/2026
2.0.7 95 3/27/2026
2.0.6 101 3/21/2026
2.0.5 89 3/21/2026
2.0.4 85 3/20/2026
2.0.3 88 3/20/2026
2.0.2 90 3/18/2026
2.0.1 85 3/18/2026
1.2.13 100 3/10/2026
1.2.12 88 3/9/2026
Loading failed