Location URI with 201 response in Ktor - kotlin

I can't seem to find how to build the URL of a newly created resource in Ktor.
post("/resources") {
val newResRequest = call.receive<Resource>()
val newResLocation = service.create(newResRequest)
.id
.let { constructAUrlWith(it) }
with(call) {
response.header("Location", newResLocation)
respond(Created)
}
Coming from the Spring world, where URLs can be constructed without knowing the request context or hostnames and such, I was wondering how could I achieve something similar in Ktor. Thanks in advance.

Related

Ktor Resources type-safe routing plugin to generate URLs and paths

I have recently updated current Ktor version to 2.1.x and Locations plugin is now deprecated. I'm refactoring code to use Resources plugin for type-safe routing. The documentation is lacking how to generate paths from Resources objects. I'm trying to create a redirect address based on a Resource and achieve a functionality like in Locations plugin to build URLs
val path = application.locations.href(Listing(name = "movies", page = 10, count = 20))
Resource definition
#Serializable
#Resource("clients")
class Clients() {
#Serializable
#Resource("{clientID}")
class ID(val parent: Clients, val clientID: Int) {}
}
Route definition
fun Route.clientRouting(dao: ClientDao) {
route("") {
get<Clients> {
call.respondText("Hello Client!")
}
get<Clients.ID> { clientID ->
call.respondRedirect(...) //Redirect to Clients route
}
}
}
So... Is there a standard way of generating paths and URLs from Resources?
You can call the href method on an Application instance to get a URL for a specified resource:
get<Clients.ID> { clientID ->
call.respondRedirect(call.application.href(Clients()))
}

How to list configured routes in Ktor

I am setting up a project with multiple modules that contain different versions of api.
To verify correct route configuration I would like to print configured routes to application log like it is done in spring framework.
Is it possible and what should I use for that?
You can do it by recursively traversing routes starting from the root all the way down the tree and filtering in only ones with the HTTP method selector. This solution is described here.
fun Application.module() {
// routing configuration goes here
val root = feature(Routing)
val allRoutes = allRoutes(root)
val allRoutesWithMethod = allRoutes.filter { it.selector is HttpMethodRouteSelector }
allRoutesWithMethod.forEach {
println("route: $it")
}
}
fun allRoutes(root: Route): List<Route> {
return listOf(root) + root.children.flatMap { allRoutes(it) }
}
Please note that the code above must be placed after the routing configuration.

Wiremock proxying to different Url like Apache ProxyPass

I am trying to achieve something very simple:
Proxy a request to
mock.com/foo?paramA=valueA&paramB=valueB
to
backend.com/bar?paramA=valueA&paramB=valueB
And I would like to do this with a json config.
The problem is that proxyBaseUrl always takes the FULL Url from the input and appends it, so
{
"request": {
"method": "GET",
"urlPattern": "/foo/.*"
},
"response": {
"proxyBaseUrl": "http://backend.com/bar"
}
}
I get a request to
http://backend.com/bar/foo?paramA=valueA&paramB=valueB
which is obviously not what I need.
I need some way to grab part of the request url with a capture group, e.g.
"urlPattern": "/foo/(.*)"
and then a way to insert the captured group - and only that - into the target url path.
How can this be done, with a JSON config?
I have checked the wiremock documentation and browsed a dozen discussions but it's still not clear to me.
These two postings had the same question and did not receive any answers:
https://groups.google.com/g/wiremock-user/c/UPO2vw4Jmhw/m/Rx0e8FtZBQAJ
https://groups.google.com/g/wiremock-user/c/EVw1qK7k8Fo/m/5iYg1SQEBAAJ
So I am wondering if this is at all possible in wiremock? (in Apache it's a 2-liner)
As far as I know, proxying isn't configurable in this way. Looking at the documentation, WireMock will only proxy the same request via the proxyBaseUrl.
Unfortunately, it looks like your best bet is going to be to write a custom response transformer that does this redirect for you. I don't think the request/response objects given in the transformer class will handle redirection on their own, so you will probably need to set up your own client to forward the requests.
Psuedo code like:
class MyCustomTransformer extends ResponseTransformer {
public String getName() {
return "MyCustomTransformer";
}
#Override
public Response transform(Request request, Response response, FileSource files, Parameters parameters) {
Pattern pattern = Pattern.compile("/regex/url/to/match/");
Matcher matcher = pattern.matcher(request.getUrl());
if (matcher.matches()) {
// Code to modify request and send via your own client
// For the example, you've saved the returned response as `responseBody`
return Response.Builder.like(response).but().body(responseBody.toJSONString()).build();
} else {
return response
}
}
}

Spring Cloud Gateway Custom Filter : WebClient.create().post() causes hanging when testing

So I've created a custom filter that, when accessed, will create a webflux client and post to a predetermined url. This seems to work fine when running, but when testing this code the test is hanging (until I cancel the test). So I feel there is a possible memory leak on top of not being able to complete the test to make sure this route is working properly. If I switch the WebClient method to get() then a resulting test of the filter works fine. Something with a post() I am not sure what is missing.
#Component
class ProxyGatewayFilterFactory: AbstractGatewayFilterFactory<ProxyGatewayFilterFactory.Params>(Params::class.java) {
override fun apply(params: Params): GatewayFilter {
return OrderedGatewayFilter(
GatewayFilter { exchange, chain ->
exchange.request.mutate().header("test","test1").build()
WebClient.create().post()
.uri(params.proxyBasePath)
.body(BodyInserters.fromDataBuffers(exchange.request.body))
.headers { it.addAll(exchange.request.headers) }
.exchange()
.flatMap {
println("the POST statusCode is "+it.statusCode())
Mono.just(it.statusCode().is2xxSuccessful)
}
.map {
exchange.request.mutate().header("test", "test2").build()
println("exchange request uri is " + exchange.request.uri)
println("exchange response statusCode is "+ exchange.response.statusCode)
exchange
}
.flatMap(chain::filter)
}, params.order)
}
Taken from the documentation, if using exchange you have an obligation to consume the body.
Unlike retrieve(), when using exchange(), it is the responsibility of the application to consume any response content regardless of the scenario (success, error, unexpected data, etc). Not doing so can cause a memory leak. The Javadoc for ClientResponse lists all the available options for consuming the body. Generally prefer using retrieve() unless you have a good reason for using exchange() which does allow to check the response status and headers before deciding how to or if to consume the response.
Spring framework 5.2.9 Webclient
This api has been changed in the latest version of the spring framework 5.3.0 now spring will force you to consume the body, because developers didn't actually read the docs.

Reactive way to Proxy request in Spring webflux

I am having a requirement in which i have to forward a request to different endpoint by adding some extra headers(usually OAuth tokens).
i tried below working one to proxying request.
fun proxy(request: ServerRequest, url:String, customHeaders: HttpHeaders = HttpHeaders.EMPTY): Mono<ServerResponse> {
val modifiedHeaders = getHeadersWithoutOrigin(request, customHeaders)
var webClient = clientBuilder.method(request.method()!!)
.uri(url)
modifiedHeaders.forEach{
val list = it.value.iterator().asSequence().toList()
val ar:Array<String> = list.toTypedArray()
webClient.header(it.key, *ar)
}
return webClient
.body(request.bodyToMono(), DataBuffer::class.java).exchange()
.flatMap { clientResponse ->
ServerResponse.status(clientResponse.statusCode())
.headers{
it.addAll(clientResponse.headers().asHttpHeaders())
}
.body(clientResponse.bodyToMono(), DataBuffer::class.java)
}
}
Incoming requests always hit one proxy endpoint at my server with target url in header. At server, i read target url and add OAuth tokens and forward request to target URL. In this scenario, i do not want to parse response body. Send the response as it is down stream.
What is the reactive way to do it?