mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-27 07:58:45 -07:00
feat: Update room cloning API to accept multiple content types for name input; enhance compatibility with 2023 client
This commit is contained in:
@@ -186,9 +186,12 @@ public sealed class RoomSave2023Tests : IClassFixture<DorkNetServerFactory>
|
|||||||
|
|
||||||
private static async Task<long> CloneAndAssertAsync(HttpClient client, long sourceId, string name)
|
private static async Task<long> CloneAndAssertAsync(HttpClient client, long sourceId, string name)
|
||||||
{
|
{
|
||||||
|
// The real 2023 client posts the name as x-www-form-urlencoded
|
||||||
|
// (`name=...`), NOT JSON — testing with JSON would miss the 415 that
|
||||||
|
// a [FromBody] handler returns for a form POST.
|
||||||
using var resp = await client.PostAsync(
|
using var resp = await client.PostAsync(
|
||||||
$"/rooms/{sourceId}/clone",
|
$"/rooms/{sourceId}/clone",
|
||||||
new StringContent(JsonSerializer.Serialize(new { name }), Encoding.UTF8, "application/json"));
|
new FormUrlEncodedContent(new Dictionary<string, string> { ["name"] = name }));
|
||||||
var text = await resp.Content.ReadAsStringAsync();
|
var text = await resp.Content.ReadAsStringAsync();
|
||||||
Assert.True(resp.IsSuccessStatusCode, $"clone of {sourceId} -> {(int)resp.StatusCode}: {text}");
|
Assert.True(resp.IsSuccessStatusCode, $"clone of {sourceId} -> {(int)resp.StatusCode}: {text}");
|
||||||
|
|
||||||
|
|||||||
@@ -959,12 +959,17 @@ public class RoomsModerationController(DorkNetDbContext db) : ControllerBase
|
|||||||
/// <see cref="DorkNet.Server.Controllers.API.Rooms.V2.RoomsController"/>;
|
/// <see cref="DorkNet.Server.Controllers.API.Rooms.V2.RoomsController"/>;
|
||||||
/// this handler proxies via the service so we don't duplicate the
|
/// this handler proxies via the service so we don't duplicate the
|
||||||
/// substantial clone-rooms logic.</summary>
|
/// substantial clone-rooms logic.</summary>
|
||||||
|
// The 2023 client posts the new name as an x-www-form-urlencoded field
|
||||||
|
// (`name=...`), NOT JSON. A `[FromBody]` parameter demands a JSON
|
||||||
|
// content-type and makes ASP.NET reject the form POST with 415 BEFORE the
|
||||||
|
// handler runs → the client reports "Failed to clone room" and boots the
|
||||||
|
// player to their dorm. Accept all three content types and read the name
|
||||||
|
// manually from form / JSON body / query.
|
||||||
[HttpPost("rooms/{roomId:long}/clone")]
|
[HttpPost("rooms/{roomId:long}/clone")]
|
||||||
public async Task<IActionResult> BareClone(long roomId,
|
[Consumes("application/x-www-form-urlencoded", "multipart/form-data", "application/json")]
|
||||||
[FromBody] CloneBareRequest? body,
|
public async Task<IActionResult> BareClone(long roomId)
|
||||||
[FromForm(Name = "Name")] string? formName)
|
|
||||||
{
|
{
|
||||||
var newName = body?.Name ?? formName ?? string.Empty;
|
var newName = await ReadCloneNameAsync();
|
||||||
var source = await db.Rooms.FirstOrDefaultAsync(r => r.Id == roomId);
|
var source = await db.Rooms.FirstOrDefaultAsync(r => r.Id == roomId);
|
||||||
if (source is null) return NotFound();
|
if (source is null) return NotFound();
|
||||||
if (!source.CloningAllowed && source.CreatorPlayerId != Me)
|
if (!source.CloningAllowed && source.CreatorPlayerId != Me)
|
||||||
@@ -1084,7 +1089,34 @@ public class RoomsModerationController(DorkNetDbContext db) : ControllerBase
|
|||||||
// room".
|
// room".
|
||||||
return Ok(RoomsController.BuildRoomServerDetails(clone, clonedScenes));
|
return Ok(RoomsController.BuildRoomServerDetails(clone, clonedScenes));
|
||||||
}
|
}
|
||||||
public sealed class CloneBareRequest { public string? Name { get; set; } }
|
private async Task<string> ReadCloneNameAsync()
|
||||||
|
{
|
||||||
|
// Form: name=... (what the 2023 client sends).
|
||||||
|
if (Request.HasFormContentType)
|
||||||
|
{
|
||||||
|
foreach (var k in new[] { "name", "Name" })
|
||||||
|
{
|
||||||
|
var v = Request.Form[k].ToString();
|
||||||
|
if (!string.IsNullOrWhiteSpace(v)) return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// JSON body: { "name": "..." }.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Request.EnableBuffering();
|
||||||
|
Request.Body.Position = 0;
|
||||||
|
using var doc = await JsonDocument.ParseAsync(Request.Body);
|
||||||
|
if (doc.RootElement.ValueKind == JsonValueKind.Object)
|
||||||
|
foreach (var k in new[] { "name", "Name" })
|
||||||
|
if (doc.RootElement.TryGetProperty(k, out var v) && v.ValueKind == JsonValueKind.String)
|
||||||
|
return v.GetString() ?? string.Empty;
|
||||||
|
}
|
||||||
|
catch { /* non-JSON / empty body */ }
|
||||||
|
}
|
||||||
|
return Request.Query["name"].ToString();
|
||||||
|
}
|
||||||
|
|
||||||
// ── Bare-path bans aliases ─────────────────────────────────────────
|
// ── Bare-path bans aliases ─────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user