Spring boot: Expected json responses, getting XML responses - java

I'm running a simple Spring boot application that retrieves details of countries from a MySQL database. The initial responses I got while running the application were in json. However, after a few edits in the application.properties file, I get my reponses in XML now. Any way to revert back to json reponses? This application is a part of a microservice application I'm trying to build with Spring cloud gateway and Eureka server.
application.properties
spring.jpa.hibernate.ddl-auto = update
spring.datasource.url= jdbc:mysql://localhost:3306/countries-microservice
spring.datasource.username= root
spring.datasource.password=
spring.datasource.driver-class-name= com.mysql.cj.jdbc.Driver
spring.application.name=countries-service
server.port=3001
eureka.client.serviceUrl.defaultZone=http://localhost:3000/eureka/
CountryRepository.java
package com.example.countriesservice.repository;
import com.example.countriesservice.model.Country;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface CountryRepository extends JpaRepository<Country, String> {
Country findByCountry(String country);
}
CountryService.java
package com.example.countriesservice.service;
import java.util.List;
import com.example.countriesservice.model.Country;
import com.example.countriesservice.repository.CountryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class CountryService {
private final CountryRepository countryRepository;
#Autowired
public CountryService(CountryRepository countryRepository) {
this.countryRepository = countryRepository;
}
public List<Country> getAllCountries() {
return countryRepository.findAll();
}
public Country getCountry(String country) {
return countryRepository.findByCountry(country);
}
}
CountryController.java
package com.example.countriesservice.controller;
import com.example.countriesservice.service.CountryService;
import java.util.List;
import com.example.countriesservice.model.Country;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
#RequestMapping("/countries")
#RestController
public class CountryController {
private final CountryService countryService;
#Autowired
public CountryController(CountryService countryService) {
this.countryService = countryService;
}
#GetMapping("/getAll")
public List<Country> getAll() {
return countryService.getAllCountries();
}
#GetMapping("/{country}")
public Country getCountry(#PathVariable String country) {
return countryService.getCountry(country);
}
}
Output
Since I am still learning Spring Boot it would be great if you could explain what am I doing wrong and how to correct it in a bit detail.

Explicitly mention that a json response is required.
In CountryController.java
import org.springframework.http.MediaType;
#GetMapping(value = "/getAll", produces = { MediaType.APPLICATION_JSON_VALUE })
public List<Country> getAll() {
return countryService.getAllCountries();
}
#GetMapping(value = "/{country}", produces = { MediaType.APPLICATION_JSON_VALUE })
public Country getCountry(#PathVariable String country) {
return countryService.getCountry(country);
}

Related

Spring Boot: How to prevent URL override when using a BaseController for CRUD operations

I have a Spring Boot application and I have implemented a base controller that handles CRUD operations for all my entities. I have also created a BrandController that extends the base controller and a BrandRepository that implements CrudRepository. The problem is that when I try to access the endpoints for the BrandController such as /api/brands, I get a 404 error, but I can access them on /brands How can I fix this so that the endpoints are accessible with /api/entitys?
Here is the code for the BrandController:
package parc.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import parc.model.concrete.Brand;
import parc.repository.BrandRepository;
#RestController
#RequestMapping("/api/brands")
public class BrandController extends BaseController<Brand, BrandRepository> {
private final BrandRepository repository;
public BrandController(BrandRepository repository) {
super(repository);
this.repository = repository;
}
}
Here is the code for the BaseController:
package parc.controller;
import org.springframework.data.repository.CrudRepository;
import org.springframework.web.bind.annotation.*;
import java.util.List;
public class BaseController<T, R extends CrudRepository<T, Long>> {
private R repository;
public BaseController(R repository) {
this.repository = repository;
}
#GetMapping("/")
public List<T> getAll() {
return (List<T>) repository.findAll();
}
#PostMapping("/")
public T create(#RequestBody T entity) {
return repository.save(entity);
}
#GetMapping("/{id}")
public T getById(#PathVariable long id) {
return repository.findById(id).orElse(null);
}
#PutMapping("/{id}")
public T update(#PathVariable long id, #RequestBody T entity) {
return repository.save(entity);
}
#DeleteMapping("/{id}")
public void delete(#PathVariable long id) {
repository.deleteById(id);
}
}
And finally the code for the BrandRepository:
package parc.repository;
import org.springframework.data.repository.CrudRepository;
import parc.model.concrete.Brand;
public interface BrandRepository extends CrudRepository<Brand, Long> {
}
I'm not a pro in Spring Boot so I'll appreciate any kind of help!
What do you have in application.yml?
maybe setting the following code will work:
in application.yml:
server:
contextPath: /api
and the BrandController:
package parc.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import parc.model.concrete.Brand;
import parc.repository.BrandRepository;
#RestController
#RequestMapping("/brands")
public class BrandController extends BaseController<Brand, BrandRepository> {
private final BrandRepository repository;
public BrandController(BrandRepository repository) {
super(repository);
this.repository = repository;
}
}

Spring MVC - inject map from yaml file

I have a configuration YAML (application.yml) file which contains location data:
locations:
countries:
PL: Poland
DE: Germany
UK: UK
RU: Russia
I would like to load it so it will be available in the html select field.
I have created a following class:
package eu.test.springdemo.model;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Map;
#ConfigurationProperties(prefix = "locations")
public class CountryOptions {
private Map<String, String> countries;
public Map<String, String> getCountries() {
return countries;
}
public void setCountries(Map<String, String> countries) {
this.countries = countries;
}
}
Then I inject CountryOptions to Controller by #Autowire. However the list of countries is empty in controller.
Configuration of app is provided by class containing following annotations:
#Configuration
#EnableWebMvc
#EnableConfigurationProperties(CountryOptions.class)
#ComponentScan(basePackages="eu.test.springdemo")
public class DemoAppConfig implements WebMvcConfigurer {
Controller code
package eu.test.springdemo.mvc;
import eu.test.springdemo.model.CountryOptions;
import eu.test.springdemo.model.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
#Controller
#RequestMapping("/")
public class HelloController {
#Autowired
CountryOptions countryOptions;
#GetMapping("/")
public String showPage() {
return "main-menu";
}
#GetMapping("/showForm")
public String showForm(Model model) {
model.addAttribute("student", new Student());
model.addAttribute("countries", countryOptions.getCountries());
return "helloworld-form";
}
}
So - any ideas why list of countries is not created from yaml file?
#ConfigurationProperties is a Spring Boot feature and will not be bound to the application.yml if you aren't using it. The best solution is usually to convert to Boot.

NullPointerException: null while trying to do a MongoDB FindByID

So i'm trying to connect java spring to mongoDB and using a findById, but it always gives the null pointer error.
2020-08-04 13:54:01.893 ERROR 8312 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
at pt.project.ProvaConceito_BackEnd.mongoDB.UserService.findById(UserService.java:29) ~[classes/:na]
at pt.project.ProvaConceito_BackEnd.mongoDB.mongoDBService.getUserByID(mongoDBService.java:19) ~[classes/:na]
The structure of this project is:
Java
MongoDB
mongoDBService
UserService
Pojos
User
Repositories
UserRepository
I'm gonna share the code I have right now:
mongoDBService
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import pt.project.ProvaConceito_BackEnd.pojos.User;
#RestController
#CrossOrigin(origins="http://localhost:4200")
public class mongoDBService {
UserService userService = new UserService();
#RequestMapping("/concept/user")
public User getUserByID(Integer id) {
return userService.findById(1);
}
}
UserService
package pt.project.ProvaConceito_BackEnd.mongoDB;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pt.project.ProvaConceito_BackEnd.pojos.User;
import pt.project.ProvaConceito_BackEnd.repositories.UserRepository;
import java.util.List;
#Service
public class UserService {
#Autowired(required = false)
private UserRepository userRepository;
public void save(String nome, int idade, String morada) {
userRepository.save(new User(nome, idade, morada));
}
public List<User> findAll() {
return userRepository.findAll();
}
public long count() {
return userRepository.count();
}
public User findById(Integer id) {
return userRepository.findById(id).orElse(null);
}
public void delete(Integer id) {
userRepository.deleteById(id);
}
}
Users (Pojo)
package pt.project.ProvaConceito_BackEnd.pojos;
import lombok.Getter;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
#Document(collection = "Users")
#Getter
public class User {
#Id
private Integer id;
private String nome;
private int idade;
private String morada;
public User(String nome, int idade, String morada) {
this.nome = nome;
this.idade = idade;
this.morada = morada;
}
}
UserRepository
package pt.project.ProvaConceito_BackEnd.repositories;
import org.springframework.data.mongodb.repository.MongoRepository;
import pt.project.ProvaConceito_BackEnd.pojos.User;
public interface UserRepository extends MongoRepository<User, Integer> {
}
What am I doing wrong here? I think the problem is on mongoDBService...
in this line:
UserService userService = new UserService();
Because I think that it's not being injected, but I don't know how to solve that...
EDIT 1
I have my main class inside pt.project.ProvaConceito_BackEnd:
ProvaConceitoBackEndApplication
package pt.project.ProvaConceito_BackEnd;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class ProvaConceitoBackEndApplication {
public static void main(String[] args) {
SpringApplication.run(ProvaConceitoBackEndApplication.class, args);
}
}
Do not use new to create object that way because spring will not have any knowledge of that object and will not be able to inject it.
Since you have already annotated the UserService with #service you should use #Autowired annotation
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import pt.project.ProvaConceito_BackEnd.pojos.User;
#RestController
#CrossOrigin(origins="http://localhost:4200")
public class mongoDBService {
#Autowired
private UserService userService;
#RequestMapping("/baieuropa/user")
public User getUserByID(Integer id) {
return userService.findById(1);
}
}
Also annotate the UserRepository class with #Repository
package pt.project.ProvaConceito_BackEnd.repositories;
import org.springframework.data.mongodb.repository.MongoRepository;
import pt.project.ProvaConceito_BackEnd.pojos.User;
#Repository
public interface UserRepository extends MongoRepository<User, Integer> {
}
Remove required = false from UserService class
Make sure to have a class with #SpringBootApplication annotation in pt.project.ProvaConceito_BackEnd package.
You need to update your repository and User.class to have an ID type of string instead of Integer. This is required for mongo repositories. May not completely solve your problem but will be a step in the right direction
#Repository
public interface UserRepository extends MongoRepository<User, String> {
}
I would follow the suggestions of other contributors to remove the "new" keyword from your mongoDBService and use the #Repository annotation too.
Autowiring only works if all components that it relies on are autowired too. So in your case because the mongoDBService uses new for the UserService then it expects the Repository to use "new" too. You should instead autowire at all levels and remove the required=false from the autowiring in the UserService.
The repository requires the #Repository annotation otherwise when Spring does its component scan the repository wont be picked up without it. #Repository #RestController etc are all just stereotypes for #Component with varying degrees of additional functionality

microservices versioning with header

issue is when i am looking swagger for v1 there i can see one endpoint which is valid, but for v2 i have given two endpoints inside controller, but /allusers endpoint i am not able to see. below are the controller.
controller v1:
package com.springboot.rest.controller.v1;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.springboot.rest.dto.UserDto;
import com.springboot.rest.service.UserService;
#RestController(value = "userControllerV1")
#RequestMapping(value = "/userinfo", produces = "application/json")
public class UserController {
public static final String X_ACCEPT_VERSION_V1 = "X-Accept-Version" + "=" + "v1";
#Autowired
private UserService userService;
#GetMapping(value = "/allusers", headers = X_ACCEPT_VERSION_V1)
public List<UserDto> getUserinfo() {
List<UserDto> finalResults = userService.getAllUserInfo();
return finalResults;
}
}
controller v2:
package com.springboot.rest.controller.v2;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.springboot.rest.dto.UserDto;
import com.springboot.rest.service.UserService;
#RestController(value = "userControllerV2")
#RequestMapping(value = "/userinfo", produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {
public static final String X_ACCEPT_VERSION_V2 = "X-Accept-Version" + "=" + "v2";
#Autowired
private UserService userService;
#GetMapping(value = "/allusers", headers = X_ACCEPT_VERSION_V2)
public List<UserDto> getUserinfo() {
List<UserDto> finalResults = userService.getAllUserInfo();
return finalResults;
}
#GetMapping(value = "/message", headers = X_ACCEPT_VERSION_V2)
public String greetMessage() {
return userService.getGreetMessage();
}
}
and i don't want to change my getUserinfo() method, could anyone help?
URI paths for /allusers end point are same in both the controllers where as api endpoints should be unique through out the application. You can add version in the uri which will make it unique. For eg.
#RequestMapping(value = "/v2/userinfo", produces = MediaType.APPLICATION_JSON_VALUE)
I did many ways, but finally OpenApi and adding filter did it for me. below is the OpenApiConfig file and link for those who wants to achieve this.
#Configuration
public class OpenApiConfig {
#Bean
public OpenAPI customOpenApi() {
return new OpenAPI()
.components(new Components())
.info(new Info().title("User-Management Microservice")
.description("demo-microservice for user-management")
.termsOfService("www.abc.com")
.contact(new io.swagger.v3.oas.models.info.Contact()
.email("abc.com")
.name("user-management"))
.version("1.0"));
}
#Bean
public GroupedOpenApi v1OpenApi() {
String[] packagesToScan = {"com.springboot.rest.controller.v1"};
return GroupedOpenApi.builder().setGroup("v1 version").packagesToScan(packagesToScan).build();
}
#Bean
public GroupedOpenApi v2OpenApi() {
String[] packagesToScan = {"com.springboot.rest.controller.v2"};
return GroupedOpenApi.builder().setGroup("v2 version").packagesToScan(packagesToScan).build();
}
}
use below link for step by step explanation:
https://www.youtube.com/watch?v=Z4FwdCgik5M

How to properly #Autowire a component in a Spring Boot App

I'm putting together a simple Spring Boot app, and having an issue with an #Autowired field not "showing up".
My main app class:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SpringElasticCatalogApi {
public static void main(String[] args) {
SpringApplication.run(SpringElasticCatalogApi.class, args);
}
}
My Repository class:
import com.discover.harmony.elastic.model.Customer;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Component;
import java.util.List;
#Component
public interface CustomerRepository extends ElasticsearchRepository<Customer, String> {
public Customer findByFirstName(String firstName);
public List<Customer> findByLastName(String lastName);
}
This class ("Loaders") requires an #Autowired repository field, which is NULL:
import com.discover.harmony.elastic.model.BusinessMetadata;
import com.discover.harmony.elastic.model.Customer;
//import com.discover.harmony.elastic.repository.CustomerRepository;
import com.discover.harmony.elastic.api.CustomerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
//#Configuration
#Component
public class Loaders {
#Autowired
private CustomerRepository repository;
#PostConstruct
#Transactional
public void loadAll(){
this.repository.deleteAll();
saveCustomers();
fetchAllCustomers();
fetchIndividualCustomers();
}
private void saveCustomers() {
this.repository.save(new Customer("Alice", "Smith"));
this.repository.save(new Customer("Bob", "Smith"));
}
private void fetchAllCustomers() {
System.out.println("Customers found with findAll():");
System.out.println("-------------------------------");
for (Customer customer : this.repository.findAll()) {
System.out.println(customer);
}
System.out.println();
}
private void fetchIndividualCustomers() {
System.out.println("Customer found with findByFirstName('Alice'):");
System.out.println("--------------------------------");
System.out.println(this.repository.findByFirstName("Alice"));
System.out.println("Customers found with findByLastName('Smith'):");
System.out.println("--------------------------------");
for (Customer customer : this.repository.findByLastName("Smith")) {
System.out.println(customer);
}
}
private List<BusinessMetadata> getData() {
List<BusinessMetadata> metadata = new ArrayList<>();
metadata.add(new BusinessMetadata((long)1,"TradeLine"));
metadata.add(new BusinessMetadata((long)2,"Credit Line"));
metadata.add(new BusinessMetadata((long)3,"Other Line"));
return metadata;
}
}
What should I change, to make the #Autowire work as expected here?
The problem is that your example is not complete on implementing the ElasticSearch. To proof this, turn your CustomerRepository into a class and remove ElasticsearchRepository<Customer, String> then everything goes fine.
What you need to do is adding a new Configuration class, with #EnableElasticsearchRepositories(basePackages = "com.discover.harmony.elastic.api.CustomerRepository") to scan the provided package for Spring Data repositories.
You can find a complete example here.

Categories

Resources