Just to give a little background on the project; I’m making a Spring Boot project using Maven. I have a two microservices, one called "products" and one called "config-server" to configure the different microservices. I already have a database called "product" on my local postgres server with a table called "product", which has a bunch of items.
Unfortunately, I get the following error when I am trying to make a GET request to the http://localhost:8222/api/v1/products
URL despite setting up the endpoints:
{
"timestamp": 1436442596410,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/api/v1/products"
}
Here are some of the relevant files:
ProductController.java:
@RestController
@RequestMapping("/api/v1/products")
@RequiredArgsConstructor
public class ProductController {
private final ProductService service;
@PostMapping
public ResponseEntity<Integer> createProduct(
@RequestBody @Valid ProductRequest request
) {
return ResponseEntity.ok(service.createProduct(request));
}
@PostMapping("/purchase")
public ResponseEntity<List<ProductPurchaseResponse>> purchaseProducts(
@RequestBody List<ProductPurchaseRequest> request
) {
return ResponseEntity.ok(service.purchaseProducts(request));
}
@GetMapping("/{product-id}")
public ResponseEntity<ProductResponse> findById(
@PathVariable("product-id") Integer productId
) {
return ResponseEntity.ok(service.findById(productId));
}
@GetMapping
public ResponseEntity<List<ProductResponse>> findAll() {
return ResponseEntity.ok(service.findAll());
}
}
ProductService.java:
@Service
@RequiredArgsConstructor
public class ProductService {
private final ProductRepository repository;
private final ProductMapper mapper;
public Integer createProduct(
ProductRequest request
) {
var product = mapper.toProduct(request);
return repository.save(product).getId();
}
public ProductResponse findById(Integer id) {
return repository.findById(id)
.map(mapper::toProductResponse)
.orElseThrow(() -> new EntityNotFoundException("Product not found with ID:: " + id));
}
public List<ProductResponse> findAll() {
return repository.findAll()
.stream()
.map(mapper::toProductResponse)
.collect(Collectors.toList());
}
@Transactional(rollbackFor = ProductPurchaseException.class)
public List<ProductPurchaseResponse> purchaseProducts(
List<ProductPurchaseRequest> request
) {
var productIds = request
.stream()
.map(ProductPurchaseRequest::productId)
.toList();
var storedProducts = repository.findAllByIdInOrderById(productIds);
if (productIds.size() != storedProducts.size()) {
throw new ProductPurchaseException("One or more products does not exist");
}
var sortedRequest = request
.stream()
.sorted(Comparator.comparing(ProductPurchaseRequest::productId))
.toList();
var purchasedProducts = new ArrayList<ProductPurchaseResponse>();
for (int i = 0; i < storedProducts.size(); i++) {
var product = storedProducts.get(i);
var productRequest = sortedRequest.get(i);
if (product.getAvailableQuantity() < productRequest.quantity()) {
throw new ProductPurchaseException("Insufficient stock quantity for product with ID:: " + productRequest.productId());
}
var newAvailableQuantity = product.getAvailableQuantity() - productRequest.quantity();
product.setAvailableQuantity(newAvailableQuantity);
repository.save(product);
purchasedProducts.add(mapper.toproductPurchaseResponse(product, productRequest.quantity()));
}
return purchasedProducts;
}
}
Here is the ProductApplication.java, which is outside the controller file:
@SpringBootApplication
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
Now in my "config-server" microservice, I have some YAML files to handle the services.
Here is the gateway-service.yml file:
server:
port: 8222
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: product-service
uri: lb:http://PRODUCT-SERVICE
predicates:
- Path=/api/v1/products/**
product-service.yml:
server:
port: 8050
spring:
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/product
username: XXX
password: XXX
jpa:
hibernate:
ddl-auto: validate
database: postgresql
database-platform: org.hibernate.dialect.PostgreSQLDialect
flyway:
baseline-on-migrate: true
enabled: true
baseline-description: "init"
baseline-version: 0
user: ${spring.datasource.username}
password: ${spring.datasource.password}
And this is the application.yml file to configure the Eureka server:
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://localhost:8761/eureka
name:
value: XXX
spring:
cloud:
config:
override-system-properties: false
management:
tracing:
sampling:
probability: 1.0
What I have tried:
I have confirmed that all the microservices are deployed in the Eureka server in port 8761:
I am also pretty much 100% sure that there is no error in my file hierarchy or in any of my pom.xml files.
I’ve also confirmed that the Spring Boot application is able to connect to my postgresSQL database as I was able to insert some data there using Flyway.
I would appreciate any help as to why I’m getting a 404 error when making the GET request. Thanks!
2
Answers
One reason (can’t be sure without seeing the logs) could be that gateway service, doesn’t match the request to any predicate, resulting in
404
.I think the predicate
Path=/api/v1/products/**
would only match requests such as/api/v1/products/foo
(child routes of products) but not/api/v1/products
itself. To fix that update your yml configuration to include this route as well:ı changed my dependecy. and ıt worked !