Access JSON data in PUT request - Jersey Dropwizard - java

I am using jersey dropwizard and trying to update a record as follow:
#PUT
#Path("api/v1/tasks/{taskId}")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces(MediaType.APPLICATION_JSON)
#UnitOfWork
public Task updateMyTask(#PathParam("taskId") long taskId, #QueryParam("description") String description) {
...
System.out.println(description); // Always `null`
...
}
My request data:
{ "description": "dummy description" }
My problem is that I am unable to access the data coming in PUT request. It always shows as null. I already have tried this with #FormParam, no luck.
EDIT:
After Sam's response, I have made suggested changes and getting following exceptions:
#PUT
#Path("api/v1/tasks/{taskId}")
#Consumes(MediaType.APPLICATION_JSON)
#UnitOfWork
public Task updateTask(Task task, #PathParam("taskId") long taskId) {
...
System.out.println(task.getDescription());
...
}
Returning error:
{
"code": 400
"message": "Unable to process JSON"
}

The data isn't a #FormParam or a #QueryParam, it's just the body of the request, so it doesn't need any annotations.
What it does need is a POJO that describes the format of the JSON data you're providing. For a simple String value that could look like this:
class Task {
String description;
... // other fields
}
Your #Consumes annotation is confused, too. You've annotated the method as #Consumes(MediaType.APPLICATION_FORM_URLENCODED), but you're trying to send it JSON data. Declare your method as:
#Consumes(MediaType.APPLICATION_JSON)
...
public Task updateMyTask(Task task, #PathParam("taskId") long taskId) {
System.out.println(task.description); // not null any more
}
You'll need to make sure you have JSON mapping enabled in Jersey.

Related

How to get Authorization header from an MethodInterpcetor on micronaut?

Before everything I tried this two solution but didn't work for me
Equivalent of javax.ws.rs NameBinding in Micronaut?
https://blogs.ashrithgn.com/custom-annotation-to-handle-authorisation-in-micronaut-aop-tutorial/
In my application I have to get a string in the Authorization header and then decode it from base64 and the json transform it into a POJO. Certainly the string is a jwt and I need to decode the public part of the json to get a data from a field.
Technically speaking a client will forward the header to me to take it, decode it and extract the data. (It's very bad practice but that's what I have to do).
For this I am using micronaut 2.4.1 and this is my code:
Interceptor:
public class HeadInterceptor implements MethodInterceptor<Object, Object> {
#SneakyThrows
#Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
Request request = (Request) context.getParameterValueMap().get("request");
// Where do i get Authorization header?
// i.e String token = (String) context.getParameterValueMap().get("Authorization");
String token = "eyJhdWQiOiJ0ZXN0IiwiaXNzIjoidGVzdCIsInN1YiI6InRlc3QiLCJleHAiOjExMTExMTEsImlhdCI6MTExMTExMTEsImRhdGEiOiJ0ZXN0In0=";
ObjectMapper mapper = new ObjectMapper();
Info info = mapper.readValue(new String(Base64.getDecoder().decode(token)), Info.class);
request.setData(info.getSub().toUpperCase());
return context.proceed();
}
}
Controller:
#Controller("/main")
public class MainController {
#Post
#Head
public Single<Response> index(#Body #Valid Request request) {
return Single.just(
Response.builder()
.message(String.format("%s-%s", request.getData(), request.getInfo()))
.build()
);
}
}
Here's a sample app https://github.com/j1cs/micronaut-jacksonxml-error
(ignore the name is for other issue)
In your implementation, the header cannot be shown in the interceptor because your index method doesn't receive it as a parameter.
So, if you add it as a parameter as below:
...
#Post
#Head
public Single<Response> index(#Body #Valid Request request, #Header("Authorization") String authorizationHeader) {
return Single.just(
Response.builder()
.message(String.format("%s-%s", request.getData(), request.getInfo()))
.build()
);
}
...
Then, you can retrieve it in the intercept method via getParameterValues(). Basically, it will be the second argument.
...
#SneakyThrows
#Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
...
String token = (String) context.getParameterValues()[1];
...
}
...
Update
Since you want your Request to contain both body and header, I edited the solution a bit. Basically, the header is added as a member variable to Request as below:
public class Request {
#NotNull
#NotBlank
private String info;
private String data;
#Header("Authorization")
String authorizationHeader;
}
Then, use #RequestBean rather than a #Body annotation on your Request parameter:
...
#Post
#Head
public Single<Response> index(#RequestBean #Valid Request request) {
return Single.just(
Response.builder()
.message(String.format("%s-%s", request.getData(), request.getInfo()))
.build()
);
}
...
Finally, you can access the header easily in your intercept() method as follows:
#SneakyThrows
#Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
...
Request request = (Request) context.getParameterValueMap().get("request");
String token = request.authorizationHeader;
...
}
I created a pull request for this change here, so you can check how it works.
In order to address the problem, you may first break the problem into parts.
Part 1: How to get arbitrary header (or list all headers)?
Try to use request.getHeaders() doc.
Part 2: How to get the header named Authorization ?
Use the way in part 1. In addition, be careful about the case. For example, is Authorization the same as authorization?
Method 2:
In controller (https://github.com/j1cs/micronaut-jacksonxml-error/blob/master/src/main/java/me/jics/MainController.java):
public Single<Response> index(#Body Request request, #Header('Authorization') String authorization) {
...
}
p.s. the "Header" annotation's doc is here: https://docs.micronaut.io/2.0.1/api/io/micronaut/http/annotation/Header.html
In interceptor:
...
String token = context.getParameterValueMap().get("authorization");
...
Why the code looks like this:
Firstly get the auth header you want using parameter injection.
Secondly, recall the fundamental concepts of AOP / AspectJ (which your interceptor class uses). Inside your interceptor, you intercept a method (in your case, the index method in controller. Thus, you can happily get the parameters of that method. In the code above, just the authorization parameter.
Please tell me if you are stuck on somewhere (and paste the code and the outputs).

Dropwizard Rest API endpoint manipulation

I have a dropwizard application to POST/GET query information. I have a #POST method that populates an arrayList with my query and its' 11 parameters. For brevity, I cut the example down to only show 3 parameters.
#Path("/query")
public class QueryResource
#GET
#Produces(MediaType.APPLICATION_JSON)
#Timed
public List<Query> getQueries() {
List<Query> queries = new ArrayList<Query>();
logger.info("Calling get queries with {} method.");
queries.add(new Query("b622d2c6-03b2-4488-9d5d-46814606e550", "eventTypeThing", "action"));
return queries;
I can send a get request through ARC and it will return successful with a json representation of the query.
I run into issues when I try to make a #GET request on the specific queryId and return a specific parameter of it. As such,
#GET
#Path("/{queryId}/action")
public Response getAction(#PathParam("queryId") String queryId, #PathParam("action") String action){
logger.info("Get action by queryId {}");
String output = "Get action: " + action;
return Response.status(200).entity(output).build();
On the rest client I make a get request to https://localhost/query/b622d2c6-03b2-4488-9d5d-46814606e550/action
I'm expecting that to return the action type of that specific queryId, but instead is returning null.
You did not declare "action" as a proper param in the #Path annotation of the method. You need to change that to:
#Path("/{queryId}/{action}")

Customizing JAX-RS response when a ConstraintViolationException is thrown by Bean Validation

Bean Validation is a good option to validate objects, but how to customize the response of a REST API (using RESTeasy) when a ConstraintViolationException is thrown?
For example:
#POST
#Path("company")
#Consumes("application/json")
public void saveCompany(#Valid Company company) {
...
}
A request with invalid data will return a HTTP 400 status code with the following body:
[PARAMETER]
[saveCompany.arg0.name]
[{company.name.size}]
[a]
It's nice but not enough, I would like to normalize these kind of errors in a JSON document.
How can I customize this behavior?
With JAX-RS can define an ExceptionMapper to handle ConstraintViolationExceptions.
From the ConstraintViolationException, you can get a set of ConstraintViolation, that exposes the constraint violation context, then map the details you need to an abitrary class and return in the response:
#Provider
public class ConstraintViolationExceptionMapper
implements ExceptionMapper<ConstraintViolationException> {
#Override
public Response toResponse(ConstraintViolationException exception) {
List<ValidationError> errors = exception.getConstraintViolations().stream()
.map(this::toValidationError)
.collect(Collectors.toList());
return Response.status(Response.Status.BAD_REQUEST).entity(errors)
.type(MediaType.APPLICATION_JSON).build();
}
private ValidationError toValidationError(ConstraintViolation constraintViolation) {
ValidationError error = new ValidationError();
error.setPath(constraintViolation.getPropertyPath().toString());
error.setMessage(constraintViolation.getMessage());
return error;
}
}
public class ValidationError {
private String path;
private String message;
// Getters and setters
}
If you use Jackson for JSON parsing, you may want to have a look at this answer, showing how to get the value of the actual JSON property.

How to parse RESTful API params with Dropwizard

Let's say I have:
#GET
public UserList fetch(#PathParam("user") String userId) {
// Do stuff here
}
Now, let's say I have my own type for userId, let's call it UserId. Is it possible to parse that String to UserId when it is passed into the fetch method, i.e.:
#GET
public UserList fetch(#PathParam("user") UserId userId) {
// Do stuff here
}
I realize I can parse the String once I am inside the method, but it would be more convenient that my method gets the type I want.
Well you've attempted to make a GET call with a request body is what I find not very helpful. Do read Paul's answer here -
you can send a body with GET, and no, it is never useful to do so
What would be good to practice is, to make a PUT or a POST call (PUT vs POST in REST) as follows -
#POST
#Path("/some-path/{some-query-param}")
public Response getDocuments(#ApiParam("user") UserId userId,
#PathParam("some-query-param") String queryParam) {
UserId userIdInstance = userId; // you can use the request body further
Note - The ApiParam annotation used is imported from the com.wordnik.swagger.annotations package. You can similarily use FormParam,QueryParam according to your source of input.
Dropwizard is using Jersey for HTTP<->Java POJO marshalling. You could use the various annotations from Jersey #*Param (#FormParam, #QueryParam, etc.) for some of the parameters.
If you need to use map/marshall to/from Java POJOs take a look at the test cases in Dropwizard:
#Path("/valid/")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class ValidatingResource {
#POST
#Path("foo")
#Valid
public ValidRepresentation blah(#NotNull #Valid ValidRepresentation representation, #QueryParam("somethingelse") String xer) {
return new ValidRepresentation();
}
This defines an API endpoint responding to HTTP POST method which expects ValidRepresentation object and "somethingelse" as HTTP method query parameter. The endpoint WILL respond ONLY when supplied with JSON parameters and will return only JSON objects (#Produces and #Consumes on the class level). The #NotNull requires that object to be mandatory for the call to succeed and #Valid instructs Dropwizard to call Hibernate validator to validate the object before calling the endpoint.
The ValidRepresentation class is here:
package io.dropwizard.jersey.validation;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.validator.constraints.NotEmpty;
public class ValidRepresentation {
#NotEmpty
private String name;
#JsonProperty
public String getName() {
return name;
}
#JsonProperty
public void setName(String name) {
this.name = name;
}
}
The POJO is using Jackson annotations to define how JSON representation of this object should look like. #NotEmtpy is annotation from Hibernate validator.
Dropwizard, Jersey and Jackson take care of the details. So for the basic stuff this is all that you need.

Submit JSON data as request body in an Apache CXF REST Service

I know this has been asked before, but going through multiple answers did not help me solve my issue. So here it is: I'm a newbie trying to create a REST service using Apache CXF. I'm trying to write a POST method and send the data as JSON in the request body(using POSTMAN in Google Chrome to do this).
My interface looks something like this:
#Path("/")
#Produces("application/json")
public interface MyService{
#POST
#Path("/addNote/{id}")
#Consumes("application/json")
NoteResponse addNote(#PathParam("id") Long id, #QueryParam("note")Note note);
// OTHER #GET METHODS THAT WORK WELL
}
My implmentation:
#WebService(name = "testservice")
public class MyServiceImpl implements MyService{
#Override
public NoteResponse addNote(Long id, Note note){
// SOME IMPLEMENTATION
}
// OTHER #GET METHOD IMPLEMENTATIONS THAT WORK
}
I've read in some answers that I do not need the #QueryParam on my note annotation, instead just put and #XMLRootElement on my Note class, but doing that and trying going on localhost:8080/rest/addNote/1 will NOT call my addNote method.
The problem I am facing now is that the note parameter comes null.
Here's the JSON I've sent via POSTMAN:
{
"note":{
"id": 4958,
"anotherId": 7886,
"comment": "salut",
"mail": "mail#mail.com",
"gregorianDate": "01-01-2016",
"type": "INFO"
}
}
Please try changing your interface definition of API addNote to this:
NoteResponse addNote(#PathParam("id") Long id, Note note);
And send this JSON string via POSTMAN:
{
"id": 4958,
"anotherId": 7886,
"comment": "salut",
"mail": "mail#mail.com",
"gregorianDate": "01-01-2016",
"type": "INFO"
}
This should work.

Categories

Resources