REST Api Spring boot with mongodb how to get json? - mongodb

#GetMapping("/getAccount")
public Account validateAccount(#RequestBody) {
}
Very new to spring boot. My account file has 5+ values all strings, username, password, id, and some etc things.
Given this
{
"username": "bob"
"password": "password"
}
It should give this with 200 response code OK
{
"id": "45645646546"
"username": "bob"
"password": "password"
"status": "Single"
"filler": "filler"
}
However I'm not sure how to read the "username" and "password" json in my validateAccount function
Not really related to this question but does anyone know how to send a response code in the function? Like .sendresponseheader(400) something like that

public class AccountDTO {
#JsonIgnore
private Long id;
#NotNull
private String username;
#NotNull
private String password;
#JsonIgnore
private String status;
#JsonIgnore
private String filler;
// getters & setters
}
You may want to create a DTO (Data Transaction Object) as shown above. Here's a link to it's wiki.
Next pass map user input into this DTO using #RequestBody annotation.
#RestController
public class AccountController {
#GetMapping("/accounts")
public ResponseEntity<Account> validateAccount(#RequestBody AccountDTO accountDTO) {
return new ResponseEntity<>(accountService.validate(accountDTO), HttpStatus.OK);
}
}
Or you can use
#RestController
public class AccountController {
#GetMapping("/accounts")
public Response validateAccount(#RequestBody AccountDTO accountDTO) {
return new ResponseEntity().ok(accountService.validate(accountDTO));
}
}
The user input will be converted from json to AccountDTO using whatever JSON processor your're using most probably it'll be com.fasterxml.jackson.core.
The #JsonIgnore and #NotNull annotation will ensure only username and password fields are used and others are ignored while taking input from user.
You can pass this DTO to your service classes and use something like findByUsername() in your Business Logic and return populated AccountDTO using the below mapper function or some external libraries like Model Mapper or MapStruct.
public toAccountDTO(Account account) {
AccountDTO accountDTO = new AccountDTO();
accountDTO.setUsername(account.getUsername());
// and so on...
return accountDTO;
}
And for your last query, wrap the returned AccountDTO object in ResponseEntity wrapper to provide a proper Response Code with your payload. Here's a link to ResponseEntity Java docs.

AccountDto.java
===============
class AccountDto{
private Long id;
private String username;
private String password;
private String status;
private String filler;
//getters & setters
}
#GetMapping("/getAccount")
public ResponseEntity validateAccount(#RequestBody AccountDto accountDto) {
return new ResponseEntity<>(accountServie.validate(accountDto),HttpStatus.OK);
}
You can do your custom operations before returning the response. Take a look Best Practice of REST

For json response nothing specific just mark class with #RestController.
For #RequestBody just use a pojo to bind the values
For error code and status you can use ResponseEntity

Related

how to save parent object containing child object using spring boot in #ManyToOne unidirectional mapping?

I am new in spring boot. I've two model classes Party(parent) and PartyCategory(child). PartyCategory stores data id, labelAr and labelEn successfully.
Now I am passing child id in json request and getting null values for labelAr and labelEn in json response as pasted below. Can someone please help what and doing wrong here.
I've pasted my code as well.
Json Request:
{
"name": "Party A",
"description": "Description of Party A",
"category": {
"id": 1
}
}
Json response;
{
"id": 6,
"name": "Party A",
"description": "Description of Party A",
"category": {
"id": 1,
"labelAr": null,
"labelEn": null
}
}
Party.java:
public class Party {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
private String description;
#ManyToOne(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
#JoinColumn(name = "category_id")
private PartyCategory category;
....setters and getters
}
PartyCategory.java:-
public class PartyCategory {
#Id
#GeneratedValue
private Integer id;
private String labelAr;
private String labelEn;
...setters and getters..
Repositories:
public interface PartyCategoryRepository extends JpaRepository<PartyCategory, Integer> {
}
public interface PartyRepository extends JpaRepository<Party, Integer> {
}
Services:
public class PartyServiceImpl {
#Autowired
PartyRepository partyRepository;
public Party saveParty(Party party) {
return partyRepository.save(party);
}
Controller:
#RestController
public class PartyController {
#Autowired
PartyServiceImpl partyServiceIml;
#PostMapping(value = "/party/save")
public Party saveParty(#RequestBody Party party ) {
Party returnedParty = partyServiceIml.saveParty(party);
return returnedParty;
}
}
The problem is that the category you are posting is not recognised as being an existing category.
You can then do something like the below. Firstly, Create a Jackson converter class to customise the Json deserialisation. I was not sure if these were Spring managed but they are so you can then inject the necessary repository.
import com.fasterxml.jackson.databind.util.StdConverter;
#Component
public class CategoryConverter extends StdConverter<Integer, PartyCategory> {
#Autowired
private PartyCategoryRepository repo;
#Override
public PartyCategory convert(Integer value) {
return repo.findById(value).get();
}
}
Then update your entity as follows so that the category Json property will be handled via the converter created above. Note that in reality I would use a Jackson mix-in to apply this custom deserializer as this would avoid 'polluting' the entity class with Json processing instructions. You can look up how to do that.
#Entity
public class Party {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
private String description;
#ManyToOne
#JoinColumn(name = "category_id")
#JsonDeserialize(converter = CategoryConverter.class) //in real life use a 'mix-in'
private PartyCategory category;
}
Then you can post JSON as below where we simply specify the id of an existing category:
{
"name": "Party A",
"description": "Description of Party A",
"category": 1
}
By enhancing this solution to use mix-ins as suggested then it is possible then to cleanly separate the view model from the entity model without having to create a DTO layer which will typically largely duplicate the entity model and which will required tedious mapping code to handle the conversions.
First of all, it's not a good practice to use the same entity for the database and also for the rest services. They should be separate entities, normally the entities for the rest services are called DTO (Data Access Objects).
Now, regarding your problem, it's normal what it's happening in your code, because you overwrite the PartyCategory labelAr and labelEn associated to the ID 1 with null values when you save your new Party, because you didn't provide any value for those two labels.
return partyRepository.save(party);
If you want to avoid this problem you have to retrieve the PartyCategory data first from the database, set to the Party entity and after that to save it to the database. Something like this:
public class PartyServiceImpl {
#Autowired
PartyRepository partyRepository;
#Autowired
PartyCategoryRepository partyCategoryRepository;
public Party saveParty(Party party) {
PartyCategory partyCategory = partyCategoryRepository.findById(party.getPartyCategory().getId());
party.setPartyCategory(partyCategory);
return partyRepository.save(party);
}

Optional #Pathvariable in REST controller spring 4

I'm writing a Rest Service (HTTP Get endpoint), where in the below uri does the following
http://localhost:8080/customers/{customer_id}
fetch the details for the customer_id passed in the uri
if the customer_id is not passed (http://localhost:8080/customers), fetch all the customers details.
Code:
#RequestMapping(method = RequestMethod.GET, value = "customers/{customer_id}")
public List<Customer> getCustomers(
#PathVariable(name = "customer_id", required = false) final String customerId) {
LOGGER.debug("customer_id {} received for getCustomers request", customerId);
}
However, with the above code, for the second scenario control is flowing to getCustomers().
Note: I'm using Java8 and spring-web 4.3.10 version
Highly appreciate any help on this.
Optional #PathVariable is used only if you want to map both GET /customers/{customer_id} and GET customers into single java method.
You cannot send request which will be sent to GET /customers/{customer_id} if you don't send customer_id.
So in your case it will be:
#RequestMapping(method = RequestMethod.GET, value = {"/customers", "customers/{customer_id}"})
public List<Customer> getCustomers(#PathVariable(name = "customer_id", required = false) final String customerId) {
LOGGER.debug("customer_id {} received for getCustomers request", customerId);
}
public abstract boolean required
Whether the path variable is required.
Defaults to true, leading to an exception being thrown if the path variable is missing in the incoming request. Switch this to false if you prefer a null or Java 8 java.util.Optional in this case. e.g. on a ModelAttribute method which serves for different requests.
You can use null or Optional from java8
This may help someone that is trying to use multiple optional path variables.
If you have more than one variable, you can always accept multiple paths.
For instance:
#GetMapping(value = {"customers/{customerId}&{startDate}&{endDate}",
"customers/{customerId}&{startDate}&",
"customers/{customerId}&&{endDate}",
"customers/{customerId}&&"
})
public Customer getCustomerUsingFilter(#PathVariable String customerId, #PathVariable Optional<Date> startDate, #PathVariable Optional<Date> endDate)
Then you would call this URL using all the path separators (in this case &)
Like GET /customers/1&& or
GET /customers/1&&2018-10-31T12:00:00.000+0000 or
GET /customers/1&2018-10-31T12:00:00.000+0000& or
GET /customers/1&2018-10-31T12:00:00.000+0000&2018-10-31T12:00:00.000+0000
You should create two end-point here to handle the individual request :
#GetMapping("/customers")
public List<Customer> getCustomers() {
LOGGER.debug("Fetching all customer");
}
#GetMapping("/customers/{id}")
public List<Customer> getCustomers(#PathVariable("id") String id) {
LOGGER.debug("Fetching customer by Id {} ",id);
}
#GetMapping is equivalent to #RequestMapping(method = RequestMethod.GET) and #GetMapping("/customers/{id}") is equivalent to #RequestMapping(method = RequestMethod.GET, value = "customers/{id}")
Better approach would be like this :
#RestController
#RequestMapping("/customers")
public class CustomerController {
#GetMapping
public List<Customer> getAllCustomers() {
LOGGER.debug("Fetching all customer");
}
#GetMapping("/{id}")
public Customer getCustomerById(#PathVariable("id") String id) {
LOGGER.debug("Fetching customer by Id {} ",id);
}

BSON ObjectID (Morphia) representation in Typescript/Angular2 Project

I have a Java class representing a customer with some properties like name, adress etc.
And I have the property:
#Id
#Property("_id")
private ObjectId id;
The customer will be fetched from a mongoDB. Everything is fine and all properties are filled.
Then I try to transport data via REST to a angular2 client
I have a Customer representation in typescript
export class Customer {
public id: string <---
Mapping inside rest call in client
.map((response: Response) => <Customer> response.json())
what do I need to put here, that the property will be mapped and that I can use it within the angular client.
I've tried to install bson-objectid via npm, but I have no idea how to get it to map the id property. All the others are working fine.
Solution found!
Create a
public YourAdapterName extends XMLAdapter<String, ObjectID> {
#Override
public String marshal(ObjectId v) throws Exception {
return v.toString();
}
#Override
public ObjectId unmarshal(String v) throws Exception {
return new ObjectId(v);
}
}
This Adapter returns the string representation of the ObjectID and I can use
id: string
in Typescript.

REST Service - JSON mapping for dynamic parameters

Let us take the following JSON response which I want to return from my REST service,
{
"id" : 123,
"name" : "ABC",
}
For the above JSON response, I can create a POJO class like,
public class Student{
private long id;
private String name;
//getters and setters
}
So, I can write a GET service to return the Student object which will be then transformed as JSON.
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response get(){
Student student = new Student();
student.setId(123);
student.setName("ABC");
return Response.ok(student).build();
}
It works fine. Now I want to introduce optional parameters to my JSON response as follows,
{
"id" : 123,
"name" : "ABC",
"params" : {"param1":"xxx","param2":342}
}
Here the params in the JSON response is an Object type and the attributes of that object are not fixed. It will vary for every request like sometime it can have 3 attributes and sometime it will have none. I don't know how to create my POJO class for this requirement. Can anybody suggest me a way how to do it?
Unless you don't need anything special, you should design it as like:
public class Student{
private long id;
private String name;
//getters and setters
private Map<String, String> parameters = new HashMap<>();
public void add(String key, String value) {
parameters.put(key, value);
}
public void addAll(Map<String, String> map) {
parameters.putAll(map);
}
}
If you need type safety then the design is little bit complicated a consider using something like:
class StudentParameters {
long param1;
String param2;
}
and Student:
public class Student{
private long id;
private String name;
//getters and setters
private StudentParameters studentParameters;
public setStudentParameters(final StudentParameters studentParameters) {
this.studentParameters = studentParameters;
}
}
Do not create complex hierarchies e.g Map<List<List>, List<List>> it will complicate whole structure.

Spring boot REST application

I am trying to make a RESTful application in Java using Spring boot by following the tutorial here. I want to modify it so that I can extract an identifier from the URL and use it to serve requests.
So http://localhost:8080/members/<memberId> should serve me a JSON object with information about the member whose ID is <memberId>. I don't know how to
Map all http://localhost:8080/members/* to a single controller.
Extract the from the URL.
Should the logic of extracting the memberId and using it be part of the controller or a separate class, as per the MVC architecture?
I am new to Spring/Spring-boot/MVC. It is quite confusing to get started with. So please bear with my newbie questions.
Map all http://localhost:8080/members/* to a single controller.
You can use a placeholder in a request mapping to so it'll handle multiple URLs. For example:
#RequestMapping("/members/{id}")
Extract the id from the URL
You can have the value of a placeholder injected into your controller method using the #PathVariable annotation with a value that matches the name of the placeholder, "id" in this case:
#RequestMapping("/members/{id}")
public Member getMember(#PathVariable("id") long id) {
// Look up and return the member with the matching id
}
Should the logic of extracting the memberId and using it be part of the controller or a separate class, as per the MVC architecture?
You should let Spring MVC extract the member id from the URL as shown above. As for using it, you'll probably pass the URL to some sort of repository or service class that offers a findById method.
As you can see in the code below, service for customer are in one controller to get one and to add new customer.
So, you will have 2 services:
http://localhost:8080/customer/
http://localhost:8080/customer/{id}
#RestController("customer")
public class SampleController {
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public Customer greetings(#PathVariable("id") Long id) {
Customer customer = new Customer();
customer.setName("Eddu");
customer.setLastname("Melendez");
return customer;
}
#RequestMapping(value = "/{id}", method = RequestMethod.POST)
public void add(#RequestBody Customer customer) {
}
class Customer implements Serializable {
private String name;
private String lastname;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getLastname() {
return lastname;
}
}
}