-
-
Notifications
You must be signed in to change notification settings - Fork 34
Useful classes
This page details the various custom pieces of infrastructure that are employed to make development easier.
All controllers should inherit from this. It provides two things:
- The ability to look up the account ID (used as the primary key in all DB tables) after the request has passed through SessionAuthenticationHandler which looks up the request's SID header in Redis.
- An overridden
Ok()
method that returns responses structured as the game expects.
DragaliaResponse is a custom type that wraps the actual content of the response in the characteristic manner that the client expects. The following example is from /tool/get_service_status
.
{
"data_headers": {
"result_code": 1
},
"data": {
"service_status": 1
}
}
Every endpoint has a data_headers
key containing a result_code
, which is 1 for success. The actual interesting content of the endpoint is usually in data
. However, to return the above response, only the following code is required:
[HttpPost("get_service_status")]
public ActionResult<DragaliaResult> GetServiceStatus()
{
return this.Ok(new ToolGetServiceStatusData() { service_status = 1 });
}
This is because the Ok()
method is overridden in DragaliaControllerBase
:
public override OkObjectResult Ok(object? value)
{
return base.Ok(
new DragaliaResponse<object>(
value ?? throw new ArgumentNullException(nameof(value)),
ResultCode.SUCCESS
)
);
}
and DragaliaResponse
is a class that has the required structure:
[MessagePackObject(keyAsPropertyName: true)]
public class DragaliaResponse<TData> where TData : class
{
public DataHeaders data_headers { get; init; }
public TData data { get; init; }
public DragaliaResponse(TData data, ResultCode result_code = ResultCode.SUCCESS)
{
this.data = data;
this.data_headers = new(result_code);
}
[JsonConstructor]
[SerializationConstructor]
public DragaliaResponse(DataHeaders data_headers, TData data)
{
this.data_headers = data_headers;
this.data = data;
}
}
If an exception is thrown during code execution, the usual behaviour is to return a 500 status code. However, this will cause the client to show "Failed to connect to server. Try again?". This is not desirable behaviour as the exact same message is shown for 404 errors, which are quite common while the project is in development. We want to be able to show an actual error message when an exception occurs so that users can report these.
Errors are shown to the client via passing a result_code
in data_headers
that is not 1. So we have a middleware called ExceptionHandlerMiddleware
which will return a result_code
that will make the client show "Server error. Returning to title screen" for most exceptions. However, you can pass a custom result_code
by throwing a DragaliaException
with a value of your choice -- there is an enum for all possible values accepted by the client. This will allow a more specific error message to be shown and enables easier investigation of bug reports.
The client keeps track of the user's savefile by loading it from /load/index, and then updates it if an endpoint returns changed data in an update_data_list. UpdateDataService is a tool that automates the creation of these objects by examining the EntityFramework change tracker -- before you call .SaveChanges()
, every change you have made to the database is stored in the tracker. This maps nicely onto the purpose of the update_data_list, and allows us to send them without having to reconstruct every action taken to the database.
This is a service that can be injected anywhere to provide access to the account ID and viewer ID via use of IHttpContextAccessor
, which is the same mechanism as in DragaliaControllerBase
. This works because scoped services (like this one) have a lifetime equal to that of the request, and it is initialized using the claims generated after authenticating the request. Use this in new repositories and services to avoid repeatedly passing these variables down and simplify your method signatures.