Http Server binded route appears to be "/" root only

Hello,

I’ve spent some time trying to figure out if i’ve done something incorrectly. I believe i’ve isolated the issue, but would love to hear opinions prior.

My code for running an Http server in UE 5.1.1 (tried on UE5.3 as well, same results):

int32 PortNumber = 6000;
TSharedPtr<IHttpRouter, ESPMode::ThreadSafe> RouterPtr = FHttpServerModule::Get().GetHttpRouter(PortNumber);

if (!RouterPtr.IsValid())
    {
        UE_LOG(LogTemp, Error, TEXT("Unable to get router for Port [%d]"), PortNumber);
        return;
    }
IHttpRouter& Router = *RouterPtr;

const HttpPath HttpPath(TEXT("/test"));
    const FHttpRequestHandler RequestHandlerOne = [this]
    (const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete)
    {
        FString TestPath = Request.RelativePath.GetPath();
        UE_LOG(LogTemp, Warning, TEXT("Request.RelativePath.IsRoot() = %d"), Request.RelativePath.IsRoot());
        UE_LOG(LogTemp, Warning, TEXT("Test Path: %s"), *TestPath);
        TUniquePtr<FHttpServerResponse> Response = FHttpServerResponse::Create(TEXT("Hello from UE5!"), TEXT("text/plain"));
        Response->Code = EHttpServerResponseCodes::Ok;
        OnComplete(MoveTemp(Response));
        return true;
};

FHttpRouteHandle HttpRouteHandle = Router.BindRoute(HttpPath, EHttpServerRequestVerbs::VERB_GET, RequestHandlerOne);
	UE_LOG(LogTemp, Warning, TEXT("HttpRouteHandle.IsValid()= %d"),  HttpRouteHandle.IsValid());

My python code, for brevity (i also used insomnia):

import requests
url = "http://127.0.0.1:6000/test"
response = requests.get(url)
print(response.text)

I do see the Hello from UE5! output. However: for Request.RelativePath.IsRoot() it prints “1” and for Test Path: it prints /. For HttpRouteHandle.IsValid()= it prints 1 as well.

This is very important as i’m trying to deal with different routes and of course not want to deal with / only.

What am i missing or did wrong? Thank you!

Thank you!

#c++ http

bump

RelativePath is relative to the path of the route handler, so for static routes it’s always gonna end up as “/” root.

I skimmed through http server usages in engine code and it doesn’t look like this variable is ever used.

It might make more sense in the case of a dynamic route, eg. /test/:param, then if you get a request like /test/42 the relative path would be 42 or /42. It would only be considered as root if get a request to just /test/.

Even so, request gives you access to path parameters via Request.PathParams so that pretty much makes accessing the relative path useless.

Thank you for your reply! appreciate it!

However, the use case of having multiple routes and not just one, static or not, is neeed.
So one can get to /read and /write and display/42 as they save different functionalities (regardless of their GET/ POST/ DELETE, etc…)
But currently, if you want to know which route you’re in, you can’t! it just will give you the route.
You are correct that for dynamic routes, it should work. But that’s not the only use case.

Makes sense? other suggestions?

You can bind as many routes as you want, each to a different function.
No need to differenciate path in the handler code.

Router.BindRoute(FHttpPath("/read"), EHttpServerRequestVerbs::VERB_GET, RequestHandlerRead);

Router.BindRoute(FHttpPath("/write"), EHttpServerRequestVerbs::VERB_GET, RequestHandlerWrite);

Router.BindRoute(FHttpPath("/display/:foo"), EHttpServerRequestVerbs::VERB_GET, RequestHandlerDisplay);
void RequestHandlerRead(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete)
{
    //...
}

void RequestHandlerWrite(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete)
{
    //...
}

void RequestHandlerDisplay(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete)
{
    FString Foo = Request.PathParams.FindRef("foo");
    //...
}

@Chatouille Thanks for this, but basically it comes down to, if you’re inside RequestHandlerWrite or RequestHandlerDisplay and you want to know (reflect) what route you’re in, you can’t get that information. Is that valid to say?

You can maybe work around this by using a Request Preprocessor.
The original path should be available in the preprocessor before being transformed into a relative path.
Store it in a different place so you can access it later on in the handler.
Something like this maybe (not tested) :

Router.RegisterRequestPreprocessor(MyPreprocessor);
Router.BindRoute("/test", TestHandler);

bool MyPreprocessor(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete)
{
    auto& MutableReq = const_cast<FHttpServerRequest&>(Request);
    MutableReq.QueryParams.Add("OriginalPath", Request.RelativePath);
    return false;
}

bool TestHandler(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete)
{
    FString Path = Request.QueryParams.FindRef("OriginalPath");
    //...
}

Thanks for your help. This doesn’t seem to work and it’s kinda hackish, as Request is a constant. On 5.3 i can’t run your code, but when i logRequest.RelativePath it is the full route. The only hackish way i “can” work with it is using thread_local variable. But… not going to do so. Really bad programming…

very confused why this isn’t a trivial feature, or it’s a bug and relativepath should actually return all of it