How to retrieve XML using webClient in Spring? - spring

I've been trying to retrieve XML using webClient in Spring, but is has not worked out.
I've been getting this error message.
UnsupportedMediaTypeException: Content type 'application/xml;charset=UTF-8' not supported for bodyType
I think the request was ok, but I failed to get the response as an object. I got '200 ok' from the server.
And I tried to send a request with 'Talend API Tester' to see if I made a right one, I got perfect response.
And I had added '#XmlRootElement', '#XmlAttribute's to my DTO.
But, I only had gotten an empty List '[]'.
What should I do to solve this problem?
Here are my codes.
//Service
public List<SearchResponseDto> search(SearchRequestDto searchRequestDto) {
System.out.println(requestUrl);
var uri = UriComponentsBuilder.fromUriString(requestUrl)
.queryParams(searchRequestDto.toMultiValueMap())
.build()
.encode()
.toUri();
return WebClient.create()
.get()
.uri(uri)
.accept(MediaType.APPLICATION_XML)
.headers(head -> {
head.set("X-Naver-Client-Id", naverId);
head.set("X-Naver-Client-Secret", naverSecret);
})
.retrieve()
.bodyToFlux(SearchResponseDto.class)
.toStream()
.collect(Collectors.toList());
}
//responseDTO
#Data
#NoArgsConstructor
#AllArgsConstructor
public class SearchResponseDto {
private String lastBuildDate;
private int total;
private int start;
private int display;
private List<SearchItem> item;
#Data
#NoArgsConstructor
#AllArgsConstructor
public static class SearchItem {
private String title;
private String link;
private String image;
private String author;
private int price;
private int discount;
private String publisher;
private String isbn;
private String description;
private String pubdate;
}
}

You need to configure additional codec to deserialize Xml response
WebClient.builder()
.exchangeStrategies(
ExchangeStrategies.builder()
.codecs(configurer ->
configurer.defaultCodecs().jaxb2Decoder(new Jaxb2XmlDecoder())
)
.build()
)
.build();

Related

Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.List] and content type [text/html]

I'm trying to call other service from my spring boot application.
Employee class
public class Employee {
private int id;
private String name;
private String email;
private String gender;
private String status;
// Constructor, getters & setters
}
Below is my Rest controller code
#GetMapping("/getUsers")
public List<Employee> getUsers() {
RestTemplate restTemplate = new RestTemplate();
String resourceUrl = "http://gorest.co.in/public/v2/users";
ResponseEntity<List> response = restTemplate.getForEntity(resourceUrl, List.class);
List<Employee> items = response.getBody();
return items;
}
I tried adding different response type but I always get below error
org.springframework.web.client.UnknownContentTypeException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.List] and content type [text/html]
Resttemplate no support the response of 'content type [text/html;charset=UTF-8]'.
solution:
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList(
MediaType.TEXT_HTML,
MediaType.TEXT_PLAIN));

Why do I get Status 400, Bad Request on my POST URL (using postman)

I am trying to follow a Spring Boot Tutorials on youtube and I get stuck at Post.
I tried searching for fix but I can't find an specific answer why I can't access the post URL?
I tried both #PostMapping and #RequestMapping(Method = POST)
still same results.
Maybe I am accessing my URL wrong?
I am trying to Post at /api/sis/student_reg
need help, thanks!
#RestController
#RequestMapping(path = "/api/sis")
public class StudentController {
#Autowired
private StudentService studentService;
#GetMapping(path = "/student")
public List<Student> displayStudent(){
return studentService.getStudent();
}
#RequestMapping(method = RequestMethod.POST, value = "/reg_student")
public void registerStudent(#RequestBody Student student){
studentService.addStudent(student);
}
}
#Service
public class StudentService {
#Autowired
private StudentRepository studentRepository;
private Student students = new Student();
public List<Student> getStudent(){
List<Student> student = new ArrayList<>();
studentRepository.findAll()
.forEach(student::add);
return student;
}
public void addStudent(Student student){
studentRepository.save(student);
}
#Entity
#Table
public class Student {
UUID uuid = UUID.randomUUID();
#Id
#SequenceGenerator(
name = "student_sequence",
sequenceName = "student_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "student_sequence"
)
private String id;
private String FirstName;
private String LastName;
private String email;
// Method Converting UUID into string
public String genID(){
id = uuid.toString();
return id;
}
//Constructor, getters and setters
Edited again:
I receive error 400 when using the "Post" while 405 "Get" on the post URL.
apologies for the confusion.
It is not about wrong url. If that would have been the case you would get 404 Not Found error and not 400 i.e., Bad Request.
This means your request is not proper. Can you please also update the whole request body which you are using in postman and also attributes of your Student Class.

Spring Boot Rest API handling unique constraint

I have a Spring Boot Rest API. I want to create users and set a unique constraint on their email and username. That works well so far. Here are the main classes and methods:
#Entity
#NoArgsConstructor
#Getter
#Setter
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(unique = true)
#NotNull
private String email;
#Column(unique = true)
#NotNull
private String username;
#NotNull
private String password;
public User(String email, String username, String password) {
this.email = email;
this.password = password;
this.username = username;
}
}
#Data
#NoArgsConstructor
#AllArgsConstructor
public class SignupRequest {
#NotNull
private String email;
#NotNull
private String username;
#NotNull
private String password;
}
#CrossOrigin(value = "*")
#PostMapping("/signup")
public ResponseEntity<?> signup(#Valid #RequestBody SignupRequest signupRequest) {
signupService.signup(signupRequest);
return ResponseEntity.ok().build();
}
#Service
public class SignupServiceImpl implements SignupService {
#Override
public void signup(SignupRequest signupRequest) throws MessagingException, UnsupportedEncodingException {
User user = new User();
User user = new User(signupRequest.getEmail(), signupRequest.getUsername(), signupRequest.getPassword());
user = userRepository.save(user);
}
}
#Repository
#Component
public interface UserRepository extends CrudRepository<User, Long> {}
Now, the thing is, when I send a POST request to that endpoint with a username or email that already exists, I just get the http response 500 Internal Server Error. But I want to return a different status code and some Error message indicating that the email/username already exists.
Now two questions:
How can I modify the response globally? I could surround the userRepository.save(user) method with a try catch block, but I would have to do that in all the methods where I save a user separately. Can I define something like that globally?
The userRepository.save(user) method just returns a. JdbcSQLIntegrityConstraintViolationException with a pretty verbose message. Is there a way to clearly determine WHAT exactly went wrong (unique username constraint failed, unique email constraint failed, ...)? I could check if a user with that username or email exists by writing a method in the userRepository, but that looks like a lot of unnecessary sql queries to me. Is there a better way?
To answer your first question, You can handle exception globally via spring exception handling mechanism. You could use spring ControllerAdvice. Here you can set generic error response and custom http code. Here is an example of ControllerAdvice
#ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler
{
#ExceptionHandler(UserNotFoundException.class)
public final ResponseEntity<ErrorResponse> handleUserNotFoundException(UserNotFoundException ex, WebRequest request) {
String details = ex.getLocalizedMessage();
ErrorResponse error = new ErrorResponse(ApplicationConstants.RECORD_NOT_FOUND, details);
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
#ExceptionHandler(Exception.class)
public final ResponseEntity<ErrorResponse> handleAllExceptions(Exception ex, WebRequest request) {
String details = ex.getLocalizedMessage();
ErrorResponse error = new ErrorResponse(ApplicationConstants.SERVER_ERROR, details);
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
public class ErrorResponse
{
public ErrorResponse(String message, String details) {
super();
this.message = message;
this.details = details;
}
private String message;
private String details;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
}
Now about second question you can loop through all the cause and check unique constraint name to find out what exception violated. But better approach would be to check first and if found then throw error.

Spring Framework Responses from POST

What is the standard object design for accepting a POST request from a client, saving the record to the database, and then returning a response back to the client? I'm working with the Spring framework.
Should I be sending back the entity and hiding properties that aren't necessary for the response?
#RestController
public class SomeController {
private final SomeService service;
#PostMapping(value = "/post/new", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SomeEntity> post(#RequestBody final SomeEntity someEntity) {
SomeEntity savedEntity = service.save(someEntity);
return ResponseEntity.ok(savedEntity);
}
}
#Entity
#Table(name = "posts")
public class SomeEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "title")
private String title;
#Column(name = "body")
#JsonIgnore
private String body;
#JsonIgnore
#Column(name = "deleted_ind")
private boolean deleted;
#JsonIgnore
#Column(name = "author")
private String author;
#Column(name = "created_at")
private LocalDateTime createdAt;
}
or would I accept some sort of POST request object that I convert to an entity, then re-assemble the entity into a response?
#JsonIgnoreProperties(ignoreUnknown = true)
public class SomePostRequestResource {
private String title;
private String body;
private String createdAt;
}
#RequiredArgsConstructor
#RestController
public class SomeController {
private final SomeService service;
private final SomeResourceAssembler resourceAssembler;
#PostMapping(value = "/post/new", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SomePostRequestResource> post(
#RequestBody final SomePostRequestResource someResource
) {
SomeEntity savedEntity = service.convertToEntityAndSave(someResource);
SomePostRequestResource response = resourceAssembler.toResource(savedEntity);
return ResponseEntity.ok(response);
}
}
But then maybe I only want to send back the createdAt, would I hide the other properties in the SomePostRequestResource, or do I need another object to represent the response, which only has the property I want to send back?
I would also appreciate any book or article suggestions related to desigining objects for use with a RESTful API. I have seen articles concerning how to design and name the endpoints, but not so many concerning how to design the objects on the backend.
I would recommend you create a DTO class for the incoming/outgoing data containing the filed that are set/viewable by the client like:
public class SomeEntityIncomingDto {
private String title;
....
}
public class SomeEntityOutgoingDto {
private Long id;
private String title;
....
}
On the other hand, You won't need to map your persistence entities to DTOs and vice versa manually, you can use a library like ModelMapper or MapStruct that handles the conversion automatically.

How to convert from entity to dto using model mapper , with conversion from string to UUID

I need some help to map Entity to DTO using Model Mapper.
Here are my two pojos
#Data
public class ClientDTO {
private UUID id;
#NotNull
private String name;
private String description;
private String contactEmail;
}
#Data
#Entity
public class Client {
#Id
private String id;
#NotNull
private String name;
private String description;
#NotNull
private String contactEmail;
}
When am trying to convert between Client to ClientDTO id is rendered as null. I tried writing a PropertyMap and a converter but none of them is working for me.
I went through the documentation and was able to find a solution to the problem. Here is the soln.
Initialization
private PropertyMap<Client, ClientDTO> clientMap;
private ModelMapper clientToClientDtoMapper;
Defining PropertyMap and Converter
clientToClientDtoMapper = new ModelMapper();
Converter<Client, UUID> uuidConverter = new AbstractConverter<Client, UUID>() {
protected UUID convert(Client source) {
return UUID.fromString(source.getId());
}
};
clientMap = new PropertyMap<Client, ClientDTO>() {
protected void configure() {
try {
using(uuidConverter).map(source).setId(null);
} catch (Exception ex) {
System.out.println("Error.");
}
}
};
clientToClientDtoMapper.addMappings(clientMap);
Helper method to convert from Entity to DTO
private ClientDTO convertToDto(Client client) {
ClientDTO clientDTO = clientToClientDtoMapper.map(client, ClientDTO.class);
return clientDTO;
}

Resources