Dropwizard example giving 400 error when creating new resource - java

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.

Related

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

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.

How To Extract Data From JSON with Rest Template in Java

i have to extract the first 5 articles from https://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=19acc3a371d145ecb37a093f9985ea21, having a result like this:
{
"total": 5,
"articles": [
{
"source": "Ilmessaggero.it",
"title": "Title",
"author": "Author",
"url": "URL"
}
]
}
I did this, having all the JSON as String as output for the localhost...
#RequestMapping("/news")
public Article connection() {
return restTemplate.getForObject
("https://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=19acc3a371d145ecb37a093f9985ea21", Article.class);
The result in the localhost is:
{"source":null,"title":null,"author":null,"url":null}
But the problem now is, how do i put the data into the list of articles?
and how do i save them into mongodb? thanks for the effort
I solved it! SImply, the NewsAPI json of Article has a field called Source, which i was trying to parse as a string, but it was NOT! Infact, it is a field described with another object! I simply had to create a class called Source with id and name, and it works! Thanks everyone for the effort!
Here's the codes of the classes:
public class Article {
private Source source;
private String author;
private String title;
private String url;
//getters and setters
News, which has a list of articles:
public class News {
private int totalResults;
private List<Article> articles;
//getters and setters
And source, which is called in Article:
public class Source {
private String id;
private String name;
//getters and setters
Here it is! The parse code is the same of the answer. Just change the return type (Article) as News and the Article.class parameter of getForObject into News.class
A simple (i.e. missing exception handling, etc) way is as follows:
First, you need a class to represent the data you are receiving, with fields that match the API response fields, for example:
public class Article {
private String source;
private String title;
... // more fields
// getters and setters
}
The code to fetch the data from the API then looks like this:
RestTemplate template = ... // initialized earlier
ResponseEntity<Article[]> response = template.exchange(
API_URL, // url to the api
HttpMethod.GET, // use the Http verb "GET"
new HttpEntity<>(headers), // optional headers, e.g. for basic auth
Article[].class // the expected response type is Article[]
);
Article[] articles = response.getBody();
List<Article> list = Arrays.asList(articles); // if you need to use collections
Note, a ResponseEntity being non-null does not imply that the request was successful. You can use responseEntity.getStatusCode() to determine the status code of the response.
Be careful, however, since by default, RestTemplate throws an exception when a non-200 error code is recieved (HttpClientErrorException and HttpServerErrorException for 4XX and 5XX codes respectively). If you want your own custom error handling, you should call:
template.setErrorHandler(new ResponseErrorHandler() {
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
// implement here
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
// implement here
}
});
For persistence into MongoDB, you can use JPA, although JPA is not a perfect fit for MongoDB due to its inherently relational nature clashing with Mongo's non-relational structure. Something like Spring Data can more sensibly map this, and is worth looking into: https://spring.io/projects/spring-data-mongodb
EDIT - calling this code
Typically, I will create an class/interface with implementation (called ArticleResource for example) that looks like:
public class ArticleResource {
private final RestTemplate template = new RestTemplate();
public List<Article> getAllArticles() {
ResponseEntity<Article[]> response = template.exchange(API_URL, HttpMethod.GET, new HttpEntity<>(headers), Article[].class);
// some error checking here
return response.getBody() == null ? Collections.emptyList() : Arrays.asList(response.getBody());
}
}
For methods that expect a single value (e.g. findArticleByTitle(String title)) I typically return an Optional<Article> (it is bad practice to return Optional<List<T>>, as an empty list represents "no values" already).
From there in your code you can call:
ArticleResource resource = new ArticeResource();
// if you want to print all the names for example:
resource.getAllArticles().stream().map(Article::getName).forEach(System.out::println);

Spring ResponseEntity best practice

I am new to RESTful web services in general, and am learning the Spring implementation of web services.
I am particularly interested in learning how to properly use ResponseEntity return types for most of my use cases.
I have one endpoint:
/myapp/user/{id}
This endpoint supports a GET request, and will return a JSON formatted string of the User object whose ID is {id}. I plan to annotate the controller method as producing JSON.
In the case that a user with ID {id} exists, I set a status of 200, and set the JSON string of the user in the body.
In the event that no user exists with that ID, I was going to return some flavor of a 400 error, but I am not sure what to set in the body of the ResponseEntity. Since I annotate the endpoint method as producing JSON, should I come up with a generic POJO that represents an error, and return that as JSON?
You donĀ“t need to use a generic Pojo, using RequestMapping you can create different responses for every Http code. In this example I show how to control errors and give a response accordingly.
This is the RestController with the service specification
#RestController
public class User {
#RequestMapping(value="/myapp/user/{id}", method = RequestMethod.GET)
public ResponseEntity<String> getId(#PathVariable int id){
if(id>10)
throw new UserNotFoundException("User not found");
return ResponseEntity.ok("" + id);
}
#ExceptionHandler({UserNotFoundException.class})
public ResponseEntity<ErrorResponse> notFound(UserNotFoundException ex){
return new ResponseEntity<ErrorResponse>(
new ErrorResponse(ex.getMessage(), 404, "The user was not found") , HttpStatus.NOT_FOUND);
}
}
Within the getId method there is a little logic, if the customerId < 10 It should response the Customer Id as part of the body message but an Exception should be thrown when the customer is bigger than 10 in this case the service should response with an ErrorResponse.
public class ErrorResponse {
private String message;
private int code;
private String moreInfo;
public ErrorResponse(String message, int code, String moreInfo) {
super();
this.message = message;
this.code = code;
this.moreInfo = moreInfo;
}
public String getMessage() {
return message;
}
public int getCode() {
return code;
}
public String getMoreInfo() {
return moreInfo;
}
}
And finally I'm using an specific Exception for a "Not Found" error
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
In the event that no user exists with that ID, I was going to return
some flavor of a 400 error, but I am not sure what to set in the body
of the ResponseEntity. Since I annotate the endpoint method as
producing JSON, should I come up with a generic POJO that represents
an error, and return that as JSON?
This is definitely a possible solution, if you want to add e.g. a more specific reason why the request failed or if you want to add a specific I18N message or just want to generify your API to provide some abstract structure.
I myself prefer the solution #Herr Derb suggested, if there is nothing to return, don't return anything. The returned data may be completely unnecessary/unused and the receiver may just discard it if the return code is anything else than 2XX.
This may be related:
http://www.bbenson.co/post/spring-validations-with-examples/
The author describes how to validate incoming models and builds a generic error response. Maybe this is something you want to do...

Malformed JSON: Unexpected '<' in Spring

I want to produce json from JPA #Entity, I have
#Entity
#JsonAutoDetect
public class Bar implements Serializable {
#Id
#GeneratedValue
private Integer id;
private String title;
//omitting other stuff
}
my controller is
#RestController
public class BarController {
#Autowired
private BarService barService;
#RequestMapping(value = "/", method = RequestMethod.GET, headers = "Accept=application/json", produces={"application/json"})
public List<Bar> list() {
return barService.findAllBars());
}
}
I'm having this error in browser
and in Postman
what is wrong with it.
The "Malformed JSON" message is from the "Pretty" printing. Click "Raw" to see the actual response.
The actual response is a 406 Not Acceptable error (says so on your screen) with a payload of HTML (hence the unexpected < from "Pretty") that says the request has been rejected by the server.
Remove the headers = "Accept=application/json" from the #RequestMapping. The produces={"application/json"} is already telling Spring to only call this method if application/json is an acceptable response, which it likely is, but the header might say *.*, or something more complex like text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8, both of which will allow application/json.
Of course, since this is likely an AJAX call that is expecting JSON, it should have listed only application/json in the accept value. Check the code executing the AJAX call if that is not the case.
I caught up one fatal mistake which is you are getting list of Bars barService.findAllBars(), you may need to convert that list to json adding the method as
public static String toJSON(Object object)
{
if ( object == null ){
return "{}";
}
try {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(object);
}
catch (Exception e) {
e.printStackTrace();
}
return "{}";
}
Now make change as
#RequestMapping(value = "/", method = RequestMethod.GET, produces={"application/json"})
public String list() {
return toJSON(barService.findAllBars());
}
hope this works, if any issues feel free to query in comments session.
I suspect there is something amiss with your accept header in your get request. Try setting the header to
"Accept=*/*"
and see what you get back.
Use JsonFormatter https://jsonformatter.curiousconcept.com/ to test the JSON before proceeding further. Its a very good tool that validates JSON and shows you possible errors with line number.

AJAX JSON post to Spring controller returns HTTP 415

I have read through the majority of posts on StackOverflow concerning this issue, and have tried numerous fixes, with nothing ultimately solving my problem. Spring throws an HttpMediaTypeNotSupportedException: Content type 'application/json' not supported
I have a controller that is defined like so:
#RequestMapping(method = RequestMethod.POST, value = "/update")
public #ResponseBody JSONDomainResponse update(#RequestBody Model inModel)
Where the model looks like so:
public class Model implements Serializable {
private static final long serialVersionUID = 2738522159847487651L;
private String id;
private BigDecimal offset;
#JsonCreator
public Model(#JsonProperty("id") String id, #JsonProperty("offset") BigDecimal offset) {
this.id = id;
this.offset = offset;
}
public String getID() {
return id;
}
public BigDecimal getOffset() {
return offset;
}
public void setID(String id) {
this.id = id;
}
public void setOffset(BigDecimal offset) {
this.offset = offset;
}
}
The AJAX call I am attempting to use looks like this:
$.ajax({
type : 'POST',
url : '/update',
contentType : "application/json",
data : JSON.stringify({"id":"test", "offset":300})
});
I have the <mvc:annotation-driven/> configuration in my context.xml file, and I have verified that MappingJacksonHttpMessageConverter's canRead() method returns true for my model and the JSON Media Type.
I do also have the Jackson core and mapper jars specified in my classpath.
I have noticed that removing the Model parameter from the controller signature makes it so I actually reach the controller with my post, which leads me to believe there is some problem with my Model. Since there is minimal information logged, however, I can't really tell what the problem can be.
Thanks in advance.
I finally found the solution to the problem, which was trivial but entirely non-obvious.
It turns out the Model object I was attempting to deserialize was in an improperly named package, and when Spring was unable to locate the Model, it simply swallows the generated exception and returns false. I discovered this through debugging deep in Spring-MVC land, particularly the StdDeserializerProvider class.
For others out there receiving this error, I would highly recommend writing some code to verify what is happening in this class, for example:
#Test
public void testThatCanConvertUpdateModel() {
MappingJacksonHttpMessageConverter conv = new MappingJacksonHttpMessageConverter();
assertTrue(conv.canRead(YourModel.class, MediaType.APPLICATION_JSON));
}

Categories

Resources