Skip to content

Commit 59d6e15

Browse files
authored
Merge pull request #35422 from Asafima/request-body-transform
Clarify request body transforms require existing body
2 parents 1728d6e + 7357cc7 commit 59d6e15

1 file changed

Lines changed: 54 additions & 12 deletions

File tree

aspnetcore/fundamentals/servers/yarp/extensibility-transforms.md

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,35 +12,30 @@ ai-usage: ai-assisted
1212
# Request and Response Transform Extensibility
1313

1414
## Introduction
15+
1516
When proxying a request it's common to modify parts of the request or response to adapt to the destination server's requirements or to flow additional data such as the client's original IP address. This process is implemented via Transforms. Types of transforms are defined globally for the application and then individual routes supply the parameters to enable and configure those transforms. The original request objects are not modified by these transforms, only the proxy requests.
1617

17-
YARP includes a set of built-in request and response transforms that can be used. See [Transforms](./transforms.md) for more details. If those transforms are not sufficient, then custom transforms can be added.
18+
YARP includes a set of built-in request and response transforms that can be used. For more information, see <xref:fundamentals/servers/yarp/transforms>. If those transforms are not sufficient, then custom transforms can be added.
1819

1920
## `RequestTransform`
2021

2122
All request transforms must derive from the abstract base class [`RequestTransform`](xref:fundamentals/servers/yarp/transforms). These can freely modify the proxy `HttpRequestMessage`. Avoid reading or modifying the request body as this may disrupt the proxying flow. Consider also adding a parametrized extension method on `TransformBuilderContext` for discoverability and ease of use.
2223

2324
A request transform may conditionally produce an immediate response such as for error conditions. This prevents any remaining transforms from running and the request from being proxied. This is indicated by setting the `HttpResponse.StatusCode` to a value other than 200, or calling `HttpResponse.StartAsync()`, or writing to the `HttpResponse.Body` or `BodyWriter`.
2425

25-
### `AddRequestTransform`
26-
27-
[AddRequestTransform](xref:Yarp.ReverseProxy.Transforms.TransformBuilderContextFuncExtensions.AddRequestTransform*) is a `TransformBuilderContext` extension method that defines a request transform as a `Func<RequestTransformContext, ValueTask>`. This allows creating a custom request transform without implementing a `RequestTransform` derived class.
26+
<xref:Yarp.ReverseProxy.Transforms.TransformBuilderContextFuncExtensions.AddRequestTransform%2A> is a `TransformBuilderContext` extension method that defines a request transform as a `Func<RequestTransformContext, ValueTask>`. This allows creating a custom request transform without implementing a `RequestTransform` derived class.
2827

2928
## `ResponseTransform`
3029

31-
All response transforms must derive from the abstract base class [`ResponseTransform`](xref:Yarp.ReverseProxy.Transforms.ResponseTransform). These can freely modify the client `HttpResponse`. Avoid reading or modifying the response body as this may disrupt the proxying flow. Consider also adding a parametrized extension method on `TransformBuilderContext` for discoverability and easy of use.
30+
All response transforms must derive from the abstract base class <xref:Yarp.ReverseProxy.Transforms.ResponseTransform>. These can freely modify the client `HttpResponse`. Avoid reading or modifying the response body as this may disrupt the proxying flow. Consider also adding a parametrized extension method on `TransformBuilderContext` for discoverability and easy of use.
3231

33-
### `AddResponseTransform`
34-
35-
[AddResponseTransform](xref:Yarp.ReverseProxy.Transforms.TransformBuilderContextFuncExtensions.AddResponseTransform*) is a `TransformBuilderContext` extension method that defines a response transform as a `Func<ResponseTransformContext, ValueTask>`. This allows creating a custom response transform without implementing a `ResponseTransform` derived class.
32+
<xref:Yarp.ReverseProxy.Transforms.TransformBuilderContextFuncExtensions.AddResponseTransform%2A> is a `TransformBuilderContext` extension method that defines a response transform as a `Func<ResponseTransformContext, ValueTask>`. This allows creating a custom response transform without implementing a `ResponseTransform` derived class.
3633

3734
## `ResponseTrailersTransform`
3835

39-
All response trailers transforms must derive from the abstract base class [ResponseTrailersTransform](xref:Yarp.ReverseProxy.Transforms.ResponseTrailersTransform). These can freely modify the client HttpResponse trailers. These run after the response body and should not attempt to modify the response headers or body. Consider also adding a parametrized extension method on `TransformBuilderContext` for discoverability and easy of use.
40-
41-
### `AddResponseTrailersTransform`
36+
All response trailers transforms must derive from the abstract base class <xref:Yarp.ReverseProxy.Transforms.ResponseTrailersTransform>. These can freely modify the client HttpResponse trailers. These run after the response body and should not attempt to modify the response headers or body. Consider also adding a parametrized extension method on `TransformBuilderContext` for discoverability and easy of use.
4237

43-
[AddResponseTrailersTransform](xref:Yarp.ReverseProxy.Transforms.TransformBuilderContextFuncExtensions.AddResponseTrailersTransform*) is a `TransformBuilderContext` extension method that defines a response trailers transform as a `Func<ResponseTrailersTransformContext, ValueTask>`. This allows creating a custom response trailers transform without implementing a `ResponseTrailersTransform` derived class.
38+
<xref:Yarp.ReverseProxy.Transforms.TransformBuilderContextFuncExtensions.AddResponseTrailersTransform%2A> is a `TransformBuilderContext` extension method that defines a response trailers transform as a `Func<ResponseTrailersTransformContext, ValueTask>`. This allows creating a custom response trailers transform without implementing a `ResponseTrailersTransform` derived class.
4439

4540
## Request body transforms
4641

@@ -76,6 +71,53 @@ This sample requires YARP 1.1, see https://github.com/microsoft/reverse-proxy/pu
7671
});
7772
```
7873

74+
Custom transforms can only modify a request body if one is already present. They can't add a new body to a request that doesn't have one (for example, a POST request without a body or a GET request). If you need to add a body for a specific HTTP method and route, you must do so in [middleware](xref:fundamentals/middleware/index) that runs before YARP, not in a transform.
75+
76+
The following middleware demonstrates how to add a body to a request that doesn't have one:
77+
78+
```csharp
79+
public class AddRequestBodyMiddleware
80+
{
81+
private readonly RequestDelegate _next;
82+
83+
public AddRequestBodyMiddleware(RequestDelegate next)
84+
{
85+
_next = next;
86+
}
87+
88+
public async Task InvokeAsync(HttpContext context)
89+
{
90+
// Only modify specific route and method
91+
if (context.Request.Method == HttpMethods.Get &&
92+
context.Request.Path == "/special-route")
93+
{
94+
var bodyContent = "key=value";
95+
var bodyBytes = Encoding.UTF8.GetBytes(bodyContent);
96+
97+
// Create a new request body
98+
context.Request.Body = new MemoryStream(bodyBytes);
99+
context.Request.ContentLength = bodyBytes.Length;
100+
101+
// Replace IHttpRequestBodyDetectionFeature so YARP knows
102+
// a body is present
103+
context.Features.Set<IHttpRequestBodyDetectionFeature>(
104+
new CustomBodyDetectionFeature());
105+
}
106+
107+
await _next(context);
108+
}
109+
110+
// Helper class to indicate the request can have a body
111+
private class CustomBodyDetectionFeature : IHttpRequestBodyDetectionFeature
112+
{
113+
public bool CanHaveBody => true;
114+
}
115+
}
116+
```
117+
118+
> [!NOTE]
119+
> You can use `context.GetRouteModel().Config.RouteId` in middleware to conditionally apply this logic for specific YARP routes.
120+
79121
## Response body transforms
80122

81123
YARP does not provide any built in transforms for modifying the response body. However, the body can be modified by custom transforms.

0 commit comments

Comments
 (0)