diff --git a/src/main/java/cc/fascinated/api/controller/ExceptionControllerAdvice.java b/src/main/java/cc/fascinated/api/controller/ExceptionControllerAdvice.java new file mode 100644 index 0000000..c601e10 --- /dev/null +++ b/src/main/java/cc/fascinated/api/controller/ExceptionControllerAdvice.java @@ -0,0 +1,32 @@ +package cc.fascinated.api.controller; + +import cc.fascinated.api.model.ErrorResponse; +import io.micrometer.common.lang.NonNull; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ControllerAdvice +public final class ExceptionControllerAdvice { + /** + * Handle a raised exception. + * + * @param ex the raised exception + * @return the error response + */ + @ExceptionHandler(Exception.class) + public ResponseEntity handleException(@NonNull Exception ex) { + HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; // Get the HTTP status + if (ex.getClass().isAnnotationPresent(ResponseStatus.class)) { // Get from the @ResponseStatus annotation + status = ex.getClass().getAnnotation(ResponseStatus.class).value(); + } + String message = ex.getLocalizedMessage(); // Get the error message + if (message == null) { // Fallback + message = "An internal error has occurred."; + } + ex.printStackTrace(); // Print the stack trace + return new ResponseEntity<>(new ErrorResponse(status, message), status); + } +} \ No newline at end of file diff --git a/src/main/java/cc/fascinated/api/controller/PlayerController.java b/src/main/java/cc/fascinated/api/controller/PlayerController.java index 93f54f8..c2af20d 100644 --- a/src/main/java/cc/fascinated/api/controller/PlayerController.java +++ b/src/main/java/cc/fascinated/api/controller/PlayerController.java @@ -5,14 +5,13 @@ import cc.fascinated.player.impl.Player; import cc.fascinated.player.impl.Skin; import cc.fascinated.player.impl.SkinPart; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.CacheControl; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.Map; -import java.util.Objects; import java.util.concurrent.TimeUnit; @RestController @@ -27,10 +26,10 @@ public class PlayerController { } @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody - public ResponseEntity getPlayer(@PathVariable String id) { + public ResponseEntity getPlayer(@PathVariable String id) { Player player = playerManagerService.getPlayer(id); if (player == null) { - return ResponseEntity.notFound().build(); + return new ResponseEntity<>(Map.of("error", "Player not found"), HttpStatus.NOT_FOUND); } return ResponseEntity.ok(player); } diff --git a/src/main/java/cc/fascinated/api/model/ErrorResponse.java b/src/main/java/cc/fascinated/api/model/ErrorResponse.java new file mode 100644 index 0000000..b806e2c --- /dev/null +++ b/src/main/java/cc/fascinated/api/model/ErrorResponse.java @@ -0,0 +1,40 @@ +package cc.fascinated.api.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.micrometer.common.lang.NonNull; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import org.springframework.http.HttpStatus; + +import java.util.Date; + +@NoArgsConstructor +@Setter +@Getter +@ToString +public final class ErrorResponse { + /** + * The status code of this error. + */ + @NonNull + private HttpStatus status; + + /** + * The message of this error. + */ + @NonNull private String message; + + /** + * The timestamp this error occurred. + */ + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss") + private Date timestamp; + + public ErrorResponse(@NonNull HttpStatus status, @NonNull String message) { + this.status = status; + this.message = message; + timestamp = new Date(); + } +} \ No newline at end of file diff --git a/src/main/java/cc/fascinated/mojang/MojangAPIService.java b/src/main/java/cc/fascinated/mojang/MojangAPIService.java index 00247d1..a1eb60c 100644 --- a/src/main/java/cc/fascinated/mojang/MojangAPIService.java +++ b/src/main/java/cc/fascinated/mojang/MojangAPIService.java @@ -35,6 +35,9 @@ public class MojangAPIService { .build(); HttpResponse response = Main.getCLIENT().send(request, HttpResponse.BodyHandlers.ofString()); + if (response.statusCode() != 200) { + return null; + } return Main.getGSON().fromJson(response.body(), new TypeToken(){}.getType()); } @@ -52,6 +55,9 @@ public class MojangAPIService { .build(); HttpResponse response = Main.getCLIENT().send(request, HttpResponse.BodyHandlers.ofString()); + if (response.statusCode() != 200) { + return null; + } return Main.getGSON().fromJson(response.body(), new TypeToken(){}.getType()); } } diff --git a/src/main/java/cc/fascinated/player/PlayerManagerService.java b/src/main/java/cc/fascinated/player/PlayerManagerService.java index 0b3cfce..4aa02c6 100644 --- a/src/main/java/cc/fascinated/player/PlayerManagerService.java +++ b/src/main/java/cc/fascinated/player/PlayerManagerService.java @@ -46,14 +46,16 @@ public class PlayerManagerService { * @return the player or null if the player does not exist */ public Player getPlayer(String id) { - UUID uuid; + UUID uuid = null; if (id.length() == 32 || id.length() == 36) { - uuid = UUID.fromString(id.length() == 32 ? UUIDUtils.addUUIDDashes(id) : id); + try { + uuid = UUID.fromString(id.length() == 32 ? UUIDUtils.addUUIDDashes(id) : id); + } catch (Exception ignored) {} } else { uuid = playerNameToUUIDCache.get(id.toUpperCase()); } - if (players.containsKey(uuid)) { + if (uuid != null && players.containsKey(uuid)) { return players.get(uuid); }