Simple put request Angular-->Java/spring not working - java

I have an objects in java backend that i want to change status property on:
#Entity
public class Employee {
#Id #GeneratedValue long id;
private String name;
private String status;
}
I want to set the property status to "Checked" for a targeted object chosen with id with this putmapping:
#PutMapping("/api/users/id")
public Employee changeStatus(#RequestParam Long id)
{
Employee newEmployee = userRepository.getById(id);
newEmployee.setStatus("Checked");
return userRepository.save(newEmployee);
}
I want to do it from my frontend through :
public changeStatus(id: number): Observable<any>
{
return this.http.put<any>(${this.apiServerUrl}/users/id, id)
}
Nothing happens in backend, no errors or anything shows up. What am i missing? I suspect i do something wrong in the frontend call, but i cant figure it out.
Backend-frontend connections seems to work because i can get all data from my backend and see it in frontend with theese two endpoints
Backend:
#RequestMapping("/api/users") public List<Employee> getUsers()
{
return (List<Employee>) userRepository.findAll();
}
Frontend:
public getUsers(): Observable<any>
{
return this.http.get<any>(${this.apiServerUrl}/users)
}
Thanks in advance!

Are you subscribing to this call on the FE ? the angular HttpClient returns an Observable, if you are not subscribing, i.e. if you are not doing something like this:
getUsers().subscribe((data) => {
console.log('whatever');
})
The http request will never be send, that's how observable works, they only get "executed" when someone is "listening"

Seeing how you try to pass id as pathVariable your endpoint should also accept it as such. For that refactor your endpoint. Frontend looks fine.
#PutMapping("/api/users/{id}")
public Employee changeStatus(#PathVariable Long id)
{
Employee newEmployee = userRepository.getById(id);
newEmployee.setStatus("Checked");
return userRepository.save(newEmployee);
}
Remove one extra id from url.
public changeStatus(id: number): Observable<any>
{
const url = this.apiServerUrl + /users/ + id;
return this.http.put<any>(url);
}
Otherwise try to get rid of any types and use strong typing - that's the benefit TypeScript gives us.

Related

Spring Boot before Put, Post and Delete Validation

I have created Post, Put and Delete Request in my controller in spring boot.
I have added validations in my model and also added #Valid parameter in method on controller.
I want to what else I am supposed to add for validation for Post, Put and Delete operation?
public class Employee {
#NotNull(message = "Employee Id can not be null")
private Integer id;
#Min(value = 2000, message = "Salary can not be less than 2000")
#Max(value = 50000, message = "Salary can not be greater than 50000")
private Integer salary;
#NotNull(message = "designation can not be null")
private String designation;
}
My Post Method is :
#PostMapping("/employees")
public ResponseEntity<Void> addEmployee(#Valid #RequestBody Employee newEmployee) {
Employee emp= service.addEmployee(newEmployee);
if (emp== null) {
return ResponseEntity.noContent().build();
}
return new ResponseEntity<Void>(HttpStatus.CREATED);
}
My Put Method is :
#PutMapping("/employees/{id}")
public ResponseEntity<Vehicle> updateEmployee(#Valid #RequestBody Employee updateEmployee) {
Employee emp= service.EmployeeById(updateEmployee.getId());
if (null == emp) {
return new ResponseEntity<Employee>(HttpStatus.NOT_FOUND);
}
emp.setSalary(updateEmployee.getSalary());
emp.setDesignation(updateEmployee.getDesignation());
service.updateEmployee(emp);
return new ResponseEntity<Employee>(emp, HttpStatus.OK);
}
Delete Method
#DeleteMapping("/employees/{id}")
public ResponseEntity<Employee> deleteEmployee(#Valid #PathVariable int id) {
Employee emp = service.getEmployeeById(id);
if (null == employee) {
return new ResponseEntity<Employee>(HttpStatus.FOUND);
}
service.deleteEmployee(id);
return new ResponseEntity<Employee>(HttpStatus.NO_CONTENT);
}
What is your specific problem?
Please refer to the following source for further reading.
Validation in Spring Boot
To your question about PUT - update does not work properly ?
Although, code looks ok. But if you are using JPA, then please remember JPA has delay data writing to database mechanism meaning it does not write data to database right away. And if you want JPA to write/save your data right away then you will have to call respository.saveAndFlush() - to force the JPA to write all data in session.
So, instead of calling the repository.saveAndFlush() every time you save data, you can simply return the same request object in this case "updateEmployee" instead of "emp" object for updating record e.g. :
return new ResponseEntity(updateEmployee, HttpStatus.OK);
POST : You should not use "#NotNull(message = "Employee Id can not be null")" on private Integer id since you are using same object for both POST and PUT method because #Valid will validate all fields in class.

How to use #PostMapping from browser?

I'm developing a simple REST service using Spring. I've a entity and a controller to that. My problem is that I can't use Post function from browser, it's just works from terminal. The Get function works fine from browser and terminal, but Post function just works from terminal, but I must that it works from browser.
For the code below, if I navigate to:
http://localhost:8080/cities
the result is ok, all records are returned.
Get method:
#RestController
public class CityController {
...
#GetMapping(value = "/cities", produces = "application/json; charset=UTF-8")
List<City> all() {
return repository.findAll();
}
}
For the Post method, just works from terminal if I write something like:
curl -X POST localhost:8080/cities -H 'Content-type:application/json'
-d '{"name":"test", "state":"test"}'
Result is ok, record is created.
But, from browser, if I tries add a new record with:
http://localhost:8080/cities?name=test&state=test
nothing happens, and no error occurs.
Post method:
#PostMapping(path = "/cities", consumes = "application/json", produces = "application/json")
City newCity(#RequestBody City city) {
return repository.save(city);
}
Entity:
#Entity
public class City {
#Id #GeneratedValue(strategy=GenerationType.AUTO) Long id;
private String name;
private String state;
public City() {
}
public City(String name, String state) {
this.name = name;
this.state = state;
}
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
Typing http://localhost:8080/cities?name=test&state=test into a browser is still going to send it as a GET.
To send as a POST, you have a few options:
Use a browser plugin as others have mentioned.
Create an HTML form.
Use JavaScript.
Option 1 is great for debugging and testing, but is no way appropriate for a production quality web site. You cannot reasonably expect your visitors to install or use a browser add-on to interact with your site.
Option 2 is the most traditional design. You would need to serve a HTML file from your application (it can be a static HTML file or use a template framework such as Thmyeleaf or Freemarker). The HTML would need a form element that is configured to use POST and point it back to your endpoint. Keep in mind your endpoint would need to accept form encoded data, not just JSON.
Option 3 could be implemented in several ways. You could have a HTML file that uses embedded JavaScript to call your endpoint, or you could use some framework like Angular or React.
Lots of options, and it's hard to say which one is best without knowing what exactly you're trying to accomplish.

Sending nested json object in POST method using Postman to Spring REST API

i am trying to send nested json object in POST request to my spring REST API.
Object java code
public class TestModel {
private String id;
private String name;
public TestModel(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}
Post method code in rest controller
#RequestMapping(value = "/helloPost")
public ResponseEntity<TestModel> helloPost(#RequestBody TestModel t) {
return new ResponseEntity<TestModel>(t, HttpStatus.OK);
}
My postman screenshot
It has to return status 200 ok and object i sent, but it returns 400 bad request permanently.
Please, tell me what am i doing wrong. It was ok when i sent one string(my #RequestBody was string too) but completly not working with custom objects.
P.S
i have added comma, no changes
You missed the "," after the id field in JSON. proper JSON is your case would be below :-
{
"id" : "1",
"name" : "test"
}
It's a malformed json you are sending to the server. You need to add comma to separate elements in json.
Even postman showing wrong icon at the left.
{
"id" : 1,
"name" : "test"
}
Also you need to add setters and default constructor in object model to set those values.
As mentioned in the comment, please add the default constructor for TestModel class. It should resolve the problem.
As an additional step, if the web service is going to accept json as input, then add consumes annotation with content type as application json.

How to access the "findById" method of a RESTful service through "getJSON"?

This is the following code of my RESTful service class:
#RequestScoped
#Path("/empresas")
public class EmpresaEndpoint {
#Inject
private EmpresaRB empresaRB;
#GET
#Path("/{id:[0-9][0-9]*}")
#Produces("application/json")
public Response findById(#PathParam("id") final Long id) {
//TODO: retrieve the empresas
Empresa empresas = null;
if (empresas == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(empresas).build();
}
#GET
#Produces("application/json")
public List<Empresa> listAll(
#QueryParam("start") final Integer startPosition,
#QueryParam("max") final Integer maxResult) {
//TODO: retrieve the empresa
return empresaRB.getEmpresas();
}
}
If I wanted to access all the data stored on "Empresa" via jQuery, I would do:
$.getJSON( "rest/empresas", function( data ) {
//whatever is needed.
}
The code above would access the "listAll" method. So how can I access the "findById" method and pass the necessary parameter?
Assuming you have a variable called empresaId that holds the id for the entity, this should work.
$.getJSON( "rest/empresas/" + empresaId, function(data) {
// Whatever is required here
}
Well without having used that particular framework, it looks like it's mapping to the right method based on the path - it will use findById if the path has an ID, e.g.
$.getJSON("rest/empresas/100", function(data) {
// ...
}
(That will find the item with ID 100... obviously substitute the ID of the item you want to find. We don't know where that's coming from, but "rest/empresas/" + id may well be all you need.)
In my initial code, there was no query being connected to the variable "empresa", on the method findById().
I created a query on the repository class and assigned it to the variable. Problem Solved.
Thank you all for the time lended.

Dropwizard example giving 400 error when creating new resource

I am new to the Dropwizard framework. I am trying to work on creating a new resource similar to person and people resource mentioned in the tutorial here https://github.com/dropwizard/dropwizard/tree/master/dropwizard-example.
I am creating a document class like this -
#Entity
#Table(name = "document")
#NamedQueries({
#NamedQuery(
name = "com.example.helloworld.core.Document.findAll",
query = "SELECT d FROM Document d"
),
#NamedQuery(
name = "com.example.helloworld.core.Document.findById",
query = "SELECT d FROM Document d WHERE d.Id = :Id"
)
})
public class Document {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long Id;
#Column(name = "ProcessingSetID")
private String ProcessingSetID;
#Column(name = "processed")
private String processed;
public long getId() {
return Id;
}
public void setId(long id) {
this.Id = id;
}
public String getProcessingSetID() {
return ProcessingSetID;
}
public void setProcessingSetID(String processingSetID) {
ProcessingSetID = processingSetID;
}
public String getProcessed() {
return processed;
}
public void setProcessed(String processed) {
this.processed = processed;
}
}
My document Dao is like this,
public Optional<Document> findById(Long id) {
return Optional.fromNullable(get(id));
}
public Document create(Document document) {
return persist(document);
}
public List<Document> findAll() {
return list(namedQuery("com.example.helloworld.core.Document.findAll"));
}
}
I am trying to call the POST method on my document resource,
#Path("/documents")
#Produces(MediaType.APPLICATION_JSON)
public class DocumentsResource {
private final DocumentDao documentDAO;
private static final Logger log = LoggerFactory.getLogger(DocumentsResource.class);
public DocumentsResource(DocumentDao documentDAO) {
this.documentDAO = documentDAO;
}
#POST
#UnitOfWork
public Document createDocument(Document document) {
log.info("inside POST method of document.");
System.out.println("inside POST method of document.....");
return documentDAO.create(document);
}
#GET
#UnitOfWork
public List<Document> listDocuments() {
return documentDAO.findAll();
}
}
But I am getting a 400 response back from my client request, please find below the client request
Client client = Client.create();
WebResource webResource = client.resource("http://localhost:8080/documents");
String input = "{\"processed\":\"new process\",\"ProcessingSetID\":\"new iD\"}";
ClientResponse response =
webResource.type("application/json").post(ClientResponse.class, input);
if (response.getStatus() != 200) {
throw new RuntimeException("Failed : HTTP error code : "
+ response.getStatus());
}
I tried to debug the problem, but the call is not reaching the POST method at the first place. It seems that it is not creating the document object from the JSON string, but i could not see a reason for that. Also when I do an entry directly in my database and make a GET call, perfect JSON string equivalent to object is received.
To get helpful message regarding 400 error, register this on jersey:
environment.jersey().register(new JsonProcessingExceptionMapper(true));
It will give more detailed message on 400 response, useful for debugging.
A little background: Dropwizard utilizes Jersey, and Jersey is what ultimately gives you back the 400 Bad Request response, probably along with a vague and laconic message.
In order to see exactly what did bother Jackson (which in turn bothered Jersey), I started out by sending a blank (empty) JSON object and see whether it was accepted (it did - and all the fields in the POJO where zero-initialized). Then I started to add fields, sending each such object along, until I reached the problematic field (in my case it was a boolean field which should have been a Boolean).
I think I can spot two difficulties in your POJO (the Document class):
The getters/setters should be annotated with #JsonProperty.
Try to change Id's type to Long (nullable long). If you are concerned about getting a null in that field, you can have the getter return a zero or any default value instead.
I faced the same issue. The errors are suppressed and not passed properly in the stack trace.
What I did was to add a try catch around the function. Then added a debugger point in the exception. I was able to figure out the exact reason.
You could try something like this.
#POST
#UnitOfWork
public Document createDocument(Document document) throws Exception{
....
}
Add debugger points in the Exception class. You will find out the exact reason of the parsing failure.
Hope I am clear and it helps!
Http status 400 means "bad request". Which it is, the json you are sending is not a valid Document.
This in turn means you will never reach the body of
#POST
#UnitOfWork
public Document createDocument(Document document){}
To solve it, try passing the json:
String input = "{\"id\":\"123456789\",\"processed\":\"new process\",\"ProcessingSetID\":\"new iD\"}";
Replace 123456789 with your actual id.
PS. it might be a good idea (depending on your scenario) to create a DTO for Document instead of passing the actual entity around.
If you are registering the Jersey's CsrfProtectionFilter in your Dropwizard *Application.java within the run(...) method, make sure you're adding the X-Requested-By header to all of your state changing HTTP calls (POST, PUT, etc). The server will return an HTTP 400 Bad Request if that header is not found in the request.

Categories

Resources