Endpoint Logic

After defining an endpoint the application logic (often also called business logic) has to be implemented for the endpoint to be useful.

Getting Request Information

All request information is supplied to the user code in the request variable:

using RestServer;
using UnityEngine;

public class Example : MonoBehaviour {
    // Reference to the RestServer instance 
    public RestServer.RestServer server;

    private void Start() {
        server.EndpointCollection.RegisterEndpoint(HttpMethod.GET, "/position", (request) => {
            var qp = request.QueryParameters // access ?a=b&c=d part
            var qpd = request.QueryParametersDict // same as above, as dictionary
            
            request.Headers; // Request Headers            
            request.Body // Access the request body directly as string
            request.BodyBytes // Access the request body as byte array for binary requests
            request.JsonBody</* Type */>() // Deserialize the request body as JSON
             
            request.CreateResponse().SendAsync(); 
        });
    }
}

Receiving binary data

While it’s not the primary use case to handle binary data, the rest server implementation still provides various methods to handle binary data.

There are two variants mentioned here, more variants are described in the FAQ.

Raw Upload

In this variant the request body is the uploaded file directly. This can easily be achieved when the client is a JavaScript application and/or a custom application where the request can be controlled completely. The binary body data can be accessed in the request via this method:

var data = request.BodyBytes

Multipart/form-data

Multipart/form-data should be the default HTML upload method when using forms in browsers with binary data. It is also the most complicated as multiple files can be uploaded with the same request and the body follows a complicated structure. It is outside the scope of the rest server library to provide a full parser for this as there are already advanced open source libraries that do this.

Nevertheless, the rest server includes a very simple parser to parse very simple multipart/form-data requests. See the Static Content Example.

List<MultiFormDataElement> uploadData;
try {
    uploadData = new SimpleMFDParser().Parse(request);
}
catch (SystemException e) {
    request.CreateResponse().InternalServerError("No form data found.").SendAsync(); 
    return;
}

var data = uploadData[0].Data;

Accessing Unity Resources

Each request is executed in a separate Thread and Unity resources can’t be accessed directly. The ThreadingHelper was created to mitigate this fact.

The ThreadingHelper can run synchronously, asynchronously and coroutine workloads in the Unity render thread. In the synchronous case the request will wait until the Unity rendering thread has executed the workload and return information back to the endpoint method. The asynchronous method is executed on the next rendered frame in Unity main render thread and doesn’t block the endpoint implementation.

In order to have an efficient Rest Server and Unity Rendering it is best to keep the amount of work inside the workload minimal.

Execute Sync example:

var position = ThreadingHelper.Instance.ExecuteSync(() => {
    return transform.position;
});

Execute Async example:

ThreadingHelper.Instance.ExecuteAsync(() => {
    transform.position += new Vector3(1.0f, 1.0f, 1.0f);
});

Execute Coroutine example:

void Start() {
    server.EndpointCollection.RegisterEndpoint(HttpMethod.GET, "/position", request => {
        ThreadingHelper.Instance.ExecuteAsyncCoroutine(Coroutine);
    });
}

private IEnumerator Coroutine() {
    yield return null;
}

See Multithreading for a deeper discussion.

Do not:

var position = ThreadingHelper.Instance.ExecuteAsync(() => {
    transform.position += Vector3.up;
    
    request.XXX; // Not allowed
});