Home > Back-end >  Error 404 Not Found. Why is my RestController not recognized from Spring Boot Application?
Error 404 Not Found. Why is my RestController not recognized from Spring Boot Application?

Time:09-17

I have the following problem. I have a simple Spring Boot Application that is supposed to process the debts of users. The service does compile, but neither a PostRequest to insert a new record nor a GetRequest to display the debt of a user works. When the service is started, the application automatically creates the appropriate table for the entity. Therefore I assume that the database connection will not be the problem. It looks like my service does not recognize the RestController. With Postman I keep getting the error code "404 Not Found".

Does anyone have any idea what I am doing wrong?

My Entity

package Entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;

@Entity
@Table(name= "debts")
@EnableAutoConfiguration
public class DebtsEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    
    @Column(name = "user_id")
    private String userId;
    @Column(name = "invoice_id")
    private int invoiceId ;
    @Column(name = "creditor")
    private String creditor;
    @Column(name = "amount")
    private double amount;
    @Column(name = "deadline")
    private String deadline;
    @Column(name = "installment")
    private double installment;
    
    
    public DebtsEntity(){
        
    }

    

    public DebtsEntity(int id, String userId, int invoiceId, String creditor, double amount, String deadline,
            double installment) {
        super();
        this.id = id;
        this.userId = userId;
        this.invoiceId = invoiceId;
        this.creditor = creditor;
        this.amount = amount;
        this.deadline = deadline;
        this.installment = installment;
    }


    public int getId() {
        return id;
    }


    public void setId(int id) {
        this.id = id;
    }


    public String getUserId() {
        return userId;
    }


    public void setUserId(String userId) {
        this.userId = userId;
    }


    public int getInvoiceId() {
        return invoiceId;
    }


    public void setInvoiceId(int invoiceId) {
        this.invoiceId = invoiceId;
    }


    public String getCreditor() {
        return creditor;
    }


    public void setCreditor(String creditor) {
        this.creditor = creditor;
    }


    public double getAmount() {
        return amount;
    }


    public void setAmount(double amount) {
        this.amount = amount;
    }


    public String getDeadline() {
        return deadline;
    }


    public void setDeadline(String deadline) {
        this.deadline = deadline;
    }


    public double getInstallment() {
        return installment;
    }


    public void setInstallment(double installment) {
        this.installment = installment;
    }


    @Override
    public String toString() {
        return "debtsEntity [id="   id   ", userId="   userId   ", invoiceId="   invoiceId   ", creditor="   creditor
                  ", amount="   amount   ", deadline="   deadline   ", installment="   installment   "]";
    }
    
}

My RestController

package Controller;

import java.net.URI;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import DAO.DebtsDAO;
import Entity.DebtsEntity;

@RestController
public class DebtsController {

    @Autowired
    DebtsDAO debtsDAO;
    
    @GetMapping("/{userId}/debts")
    public List<DebtsEntity> retrieveDebtsByUserId(@PathVariable String userId)
    {
        List<DebtsEntity> debts = debtsDAO.findByUserId(userId);
        
        return debts;
    }
    
    @PostMapping("/debts")
    public ResponseEntity<Object> createInvoice(@RequestBody DebtsEntity debtsEntity)  
    {  
        DebtsEntity savedUser =debtsDAO.save(debtsEntity);
        URI location= ServletUriComponentsBuilder.fromCurrentRequest()
        .path("path/{id}")
        .buildAndExpand(savedUser.getId()).toUri();
        
        return ResponseEntity.created(location).build();
    }  
}

My DAO

package DAO;

import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component;
import Entity.DebtsEntity;

@Component
public interface DebtsDAO extends JpaRepository<DebtsEntity, Integer>{

    List<DebtsEntity> findByUserId(String user_id);

}

My application.properties

## Database konfiguration
spring.datasource.url=jdbc:postgresql://localhost:5432/Test
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.jpa.hibernate.ddl-auto=create
# define if Database Queries should be written in logfile
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.default_schema=public
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL81Dialect

server.port=8000
spring.application.name=Debts

My dependencies

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>LifeOps</groupId>
    <artifactId>Service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Service</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

UPDATE 1

Get-Request

URL: http://localhost:8000/1000/depts

PostRequest

URL: http://localhost:8000/depts RequestBody:

{
    "id":1000,
    "user_id": "ABC1234",
    "invoice_id":100001,
    "creator": "A12L",
    "amount": 123012.56,
    "deadline": "20.10.12",
    "installment": 50.00
}
package Service;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;

@EntityScan(basePackages = "entity")
@SpringBootApplication
public class ServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceApplication.class, args);
    }

}

UPDATE 2

enter image description here

Class                                 package
DebtsApplication                      service.debts
DebtsController                       service.debts.controller
DebtsEntity                           service.debts.entity
DebtsDAO                              service.debts.dao

CodePudding user response:

I would suggest you add a base package (something like "com.doncarlito") and put ServiceApplication in it.

package com.doncarlito;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;

@EntityScan(basePackages = "entity")
@SpringBootApplication
public class ServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceApplication.class, args);
    }

}

Then move all the other existent packages to be sub-packages of this one:

  • com.doncarlito.entity
  • com.doncarlito.dao
  • com.doncarlito.controller
  • com.doncarlito.service

The reason being that @SpringBootApplication encapsulates @Configuration, @EnableAutoConfiguration, and @ComponentScan annotations with their default attributes. The default value for @ComponentScan means that all the sub-packages on the package the @ComponentScan is used are scanned. That is why it is usually a good practice to include the main class in the base package of the project.

CodePudding user response:

You are running your app from x.y.z.ApplicationService but your components are in different pacage like x.y.a.YourComponent which will NOT be picked up by the spring.

By default, spring scans package (and descendants) of your @SpringApplication class. In reference to the above it would be x.y.z.*.....

To solve your problem (alternatives)

  1. Move your ApplicationService next to DebtsApplication so it will be "on top" of all required components (@RestController for example)
  2. Add @ComponentScan(basePackgages={'service.debts'}) to ApplicationService so you will "manually" show spring where to look for components.
  3. There are other ways, but you should be fine with those 2
  • Related