Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ repositories {

dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-aop")
compileOnly("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
annotationProcessor("org.projectlombok:lombok")
}

tasks.withType<Test> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.javaspringcourse.Exception;

public record ErrorResponse(String message) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.javaspringcourse.Exception;

public class TooManyRequestsException extends RuntimeException {
public TooManyRequestsException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.javaspringcourse.Exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class TooManyRequestsExceptionHandler {

@ResponseStatus(HttpStatus.TOO_MANY_REQUESTS)
@ExceptionHandler(TooManyRequestsException.class)
public ErrorResponse handle(TooManyRequestsException e) {
return new ErrorResponse(e.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.javaspringcourse.RequestsLimit;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequestsLimit {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.javaspringcourse.RequestsLimit;

import lombok.RequiredArgsConstructor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.javaspringcourse.Exception.TooManyRequestsException;
import org.springframework.stereotype.Component;

@Component
@Aspect
@RequiredArgsConstructor
public class RequestsLimitAspect {
private final RequestsLimitService requestsLimitService;

@Before(value = "@annotation(RequestsLimit)")
private void before(JoinPoint joinPoint) {
if (!requestsLimitService.canHandleRequest(
joinPoint.getSignature().getName())) {
throw new TooManyRequestsException("Too many requests.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.javaspringcourse.RequestsLimit;

import lombok.RequiredArgsConstructor;
import org.javaspringcourse.props.RequestsLimitProps;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
@RequiredArgsConstructor
public class RequestsLimitService {
private final RequestsLimitProps requestsLimitProps;
private final Map<String, Integer> requestsCount = new HashMap<>();

public boolean canHandleRequest(String apiName) {
if (requestsCount.containsKey(apiName)) {
requestsCount.put(apiName, requestsCount.get(apiName) + 1);
} else {
requestsCount.put(apiName, 1);
}
return requestsCount.get(apiName) <= requestsLimitProps.getMax();
}
}
9 changes: 9 additions & 0 deletions src/main/java/org/javaspringcourse/config/AspectConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.javaspringcourse.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
public class AspectConfig {
}
23 changes: 23 additions & 0 deletions src/main/java/org/javaspringcourse/controller/ApiController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.javaspringcourse.controller;

import org.javaspringcourse.RequestsLimit.RequestsLimit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class ApiController {

@RequestsLimit
@GetMapping("/getHelloWorld")
public String getHelloWorld() {
return "Hello World";
}

@RequestsLimit
@GetMapping("/getByeWorld")
public String getByeWorld() {
return "Bye World";
}
}
14 changes: 14 additions & 0 deletions src/main/java/org/javaspringcourse/props/RequestsLimitProps.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.javaspringcourse.props;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "requests-limit")
public class RequestsLimitProps {
private int max;
}
1 change: 0 additions & 1 deletion src/main/resources/application.properties

This file was deleted.

5 changes: 5 additions & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
server:
port: 8080

requests-limit:
max: 2