Enable Request-Aware Responses in AdminApiMappingBuilder When Using Aspire Integration. #702

Closed
opened 2025-12-29 15:31:20 +01:00 by adam · 2 comments
Owner

Originally created by @nadzzz on GitHub (Jul 2, 2025).

Originally assigned to: @StefH on GitHub.

I'm unable to customize WireMock responses based on the incoming request content when using the AdminApiMappingBuilder which seems the only way to configure WireMock in Aspire context. The current response methods in AdminApiMappingBuilder do not provide signatures that include the request object. In contrast, the IResponseBuilder interface (and its inheritance chain, such as IBodyResponseBuilder) offers several methods that accept Func<IRequestMessage, ...> parameters, allowing dynamic response generation based on the request. This limitation makes it difficult to create flexible mocks that adapt their responses to the request data

Describe the solution you'd like

I would like the AdminApiMappingBuilder (and related APIs) to support response methods that accept delegates such as Func<IRequestMessage, string> or similar, enabling dynamic response content generation based on the request. This would bring parity with the flexibility offered by IResponseBuilder and its related interfaces

Describe alternatives you've considered

  • I have reviewed the available methods in AdminApiMappingBuilder and could not find any that allow access to the request object for dynamic response generation.
  • Using static responses or pre-defined templates, but these do not cover scenarios where the response must be computed from the request.

Is your feature request supported by WireMock (java version)? Please provide details.

I don't think Aspire compatibility has been implemented in Java.

Additional context

A pertinent use case: My application code uses the links given back by the hypermedia-driven protocol hrefs provided in the list request. The hrefs minimally need to point back to the mock base URL, which is dynamically assigned each time. For example:

        LoginRegistrationServer
            ?.Given(Request.Create().WithPath(new WildcardMatcher($"/registration/clients/*/secrets")).UsingGet())
            .RespondWith(
                Response
                    .Create()
                    .WithCallback(request =>
                    {
                        ResponseMessage responseMessage = new()
                        {
                            StatusCode = 200,
                            Headers = new Dictionary<string, WireMockList<string>> { { "Content-Type", new WireMockList<string>("application/json") } },
                            BodyData = new BodyData
                            {
                                BodyAsString = $$"""
                                {
                                  "secrets": [
                                    {
                                      "description": "Example Secret 1",
                                      "expires_at": "2023-12-31T23:59:59Z",
                                      "created_on": "2023-01-01T00:00:00Z",
                                      "type": "example-type",
                                      "_links": {
                                        "self": "{{LoginRegistrationServer.Url}}{{request.AbsolutePath}}/1"
                                      }
                                    },
                                    {
                                      "description": "Example Secret 2",
                                      "expires_at": "2024-12-31T23:59:59Z",
                                      "created_on": "2023-02-01T00:00:00Z",
                                      "type": "example-type",
                                      "_links": {
                                        "self": "{{LoginRegistrationServer.Url}}{{request.AbsolutePath}}/2"
                                      }
                                    }
                                  ],
                                  "_links": {
                                    "resetsharedsecrets": "{{LoginRegistrationServer.Url}}/secrets/reset",
                                    "self": "{{LoginRegistrationServer.Url}}{{request.AbsolutePath}}"
                                  }
                                }
                                """,
                                DetectedBodyType = BodyType.String,
                            },
                        };
                        return responseMessage;
                    })
            );
Originally created by @nadzzz on GitHub (Jul 2, 2025). Originally assigned to: @StefH on GitHub. ### **Is your feature request related to a problem? Please describe.** I'm unable to customize WireMock responses based on the incoming request content when using the **AdminApiMappingBuilder** which seems the only way to configure WireMock in Aspire context. The current response methods in **AdminApiMappingBuilder** do not provide signatures that include the request object. In contrast, the **IResponseBuilder** interface (and its inheritance chain, such as **IBodyResponseBuilder**) offers several methods that accept **Func<IRequestMessage, ...>** parameters, allowing dynamic response generation based on the request. This limitation makes it difficult to create flexible mocks that adapt their responses to the request data ### **Describe the solution you'd like** I would like the **AdminApiMappingBuilder** (and related APIs) to support response methods that accept delegates such as Func<IRequestMessage, string> or similar, enabling dynamic response content generation based on the request. This would bring parity with the flexibility offered by IResponseBuilder and its related interfaces ### **Describe alternatives you've considered** - I have reviewed the available methods in AdminApiMappingBuilder and could not find any that allow access to the request object for dynamic response generation. - Using static responses or pre-defined templates, but these do not cover scenarios where the response must be computed from the request. ### **Is your feature request supported by [WireMock (java version)](https://www.wiremock.org)? Please provide details.** I don't think Aspire compatibility has been implemented in Java. ### **Additional context** A pertinent use case: My application code uses the links given back by the hypermedia-driven protocol hrefs provided in the list request. The hrefs minimally need to point back to the mock base URL, which is dynamically assigned each time. For example: ``` c# LoginRegistrationServer ?.Given(Request.Create().WithPath(new WildcardMatcher($"/registration/clients/*/secrets")).UsingGet()) .RespondWith( Response .Create() .WithCallback(request => { ResponseMessage responseMessage = new() { StatusCode = 200, Headers = new Dictionary<string, WireMockList<string>> { { "Content-Type", new WireMockList<string>("application/json") } }, BodyData = new BodyData { BodyAsString = $$""" { "secrets": [ { "description": "Example Secret 1", "expires_at": "2023-12-31T23:59:59Z", "created_on": "2023-01-01T00:00:00Z", "type": "example-type", "_links": { "self": "{{LoginRegistrationServer.Url}}{{request.AbsolutePath}}/1" } }, { "description": "Example Secret 2", "expires_at": "2024-12-31T23:59:59Z", "created_on": "2023-02-01T00:00:00Z", "type": "example-type", "_links": { "self": "{{LoginRegistrationServer.Url}}{{request.AbsolutePath}}/2" } } ], "_links": { "resetsharedsecrets": "{{LoginRegistrationServer.Url}}/secrets/reset", "self": "{{LoginRegistrationServer.Url}}{{request.AbsolutePath}}" } } """, DetectedBodyType = BodyType.String, }, }; return responseMessage; }) ); ```
adam added the question label 2025-12-29 15:31:20 +01:00
adam closed this issue 2025-12-29 15:31:20 +01:00
Author
Owner

@StefH commented on GitHub (Jul 3, 2025):

The AdminApiMappingBuilder is "disconnected" from the C# code.

WireMock.Net runs in Aspire, and all configuration using that builder is done via the admin REST interface, and this interface does not allow Func<> callbacks.

In your case, you need to use templating to dynamically create the response.

For that LoginRegistrationServer, you can pass this as a value using the Data property in the mapping.
Like:

mappingBuilder.Given(m => m
    .WithTitle("This is my title 1")
    .WithData(new 
    {
       LoginRegistrationServer: "https://abc"
    })
    .WithRequest(req => req
        .UsingPost()
        .WithPath("/bla1")
        .WithHeader("Authorization", "*", true)
        .WithBody(body => body
            .WithMatcher(matcher => matcher
                .WithName("JsonPartialMatcher")
                .WithPattern(new { test = "abc" })
            )
        )
    )
    .WithResponse(rsp => rsp
        .WithBody("The Response")
    )
);

https://github.com/wiremock/WireMock.Net/blob/master/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs#L97

And then use this Data in the handlebars repsonse templating for the response.

@StefH commented on GitHub (Jul 3, 2025): The AdminApiMappingBuilder is "disconnected" from the C# code. WireMock.Net runs in Aspire, and all configuration using that builder is done via the admin REST interface, and this interface does not allow Func<> callbacks. In your case, you need to use templating to dynamically create the response. For that LoginRegistrationServer, you can pass this as a value using the `Data` property in the mapping. Like: ``` mappingBuilder.Given(m => m .WithTitle("This is my title 1") .WithData(new { LoginRegistrationServer: "https://abc" }) .WithRequest(req => req .UsingPost() .WithPath("/bla1") .WithHeader("Authorization", "*", true) .WithBody(body => body .WithMatcher(matcher => matcher .WithName("JsonPartialMatcher") .WithPattern(new { test = "abc" }) ) ) ) .WithResponse(rsp => rsp .WithBody("The Response") ) ); ``` https://github.com/wiremock/WireMock.Net/blob/master/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs#L97 And then use this Data in the handlebars repsonse templating for the response.
Author
Owner

@nadzzz commented on GitHub (Jul 3, 2025):

Ok, I understand the limitation of the current Aspire version. I will use templates for my immediate problem, and that will work. thanks for the help.

I do have another case where this approach won’t be sufficient. Specifically, it means we can’t use features like WithCallback to provide custom implementations, such as hosting a small in-memory repository to hold state, etc.

Aspire WireMock is very useful for stubbing many services, but it’s not a silver bullet for all external dependency scenarios when running services locally.

Still, it’s quite valuable for many use cases.
Thanks again!

@nadzzz commented on GitHub (Jul 3, 2025): Ok, I understand the limitation of the current Aspire version. I will use templates for my immediate problem, and that will work. thanks for the help. I do have another case where this approach won’t be sufficient. Specifically, it means we can’t use features like WithCallback to provide custom implementations, such as hosting a small in-memory repository to hold state, etc. Aspire WireMock is very useful for stubbing many services, but it’s not a silver bullet for all external dependency scenarios when running services locally. Still, it’s quite valuable for many use cases. Thanks again!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/WireMock.Net-wiremock#702