Make an HTTP request with body names set in #JsonProperty - java

I have a Spring Boot application with Jackson to process JSON, and I am using Retrofit2 to make HTTP requests. I would like to send body parameters with the name set in the #JsonProperty annotation:
public class OAuthTokenRequest {
#JsonProperty("oauth_consumer_key")
private String consumerKey;
#JsonProperty("oauth_nonce")
private String nonce;
#JsonProperty("oauth_timestamp")
private Long timestamp;
...
}
And this would be the request:
#POST("/oauth-service/oauth/request_token")
Call<ResponseBody> getOauthToken(#Body OAuthTokenRequest request);
In this code fragment, I would like to send the parameters as named in the annotations (oauth_consumer_key, oauth_nonce, oauth_timestamp, etc.). However, the parameters are being sent like this:

Above your class add below annotaion
#JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
public class OAuthTokenRequest {
#JsonProperty("oauth_consumer_key")
private String consumerKey;
#JsonProperty("oauth_nonce")
private String nonce;
#JsonProperty("oauth_timestamp")
private Long timestamp;
...
}

Related

Problem with status 415 in Spring MockMVC Tests after trying to send a REST Post Request

I have a REST POST endpoint which is used to create an entity. I've trying to test it with MockMVC but every time that i sent the request i received a 415 status code (media not supported):
java.lang.AssertionError: Status expected:<201> but was:<415> Expected :201 Actual :415
The endpoint accepts json body in the request and I sent this data using the MockMVC contentType as APPLICATION_JSON_VALUE and the content method with the serialized object by Jackson.
The controller ALSO is managed my Spring Security Filters but i think this is not the problem as i'm using the #AutoConfigureMockMvc(addFilters = false) and the HTTP status code is related to not supported media type and not about any security exception.
I've found a plenty of topics talking about it but none was able to solve my problem. One of the cases was including the #EnableWebMvc into the Controller OR as a configuration bean test, but none work it.
My attempt with #EnableWebMvc as test bean
#TestConfiguration
#EnableWebMvc
public class ProdutoControllerConfigurationTest {
#Bean
public ProdutoController produtoController() {
return new ProdutoController(/* dependencies by autowired */);
}
}
EDIT: I also tried with different MediaType like MediaType.APPLICATION_JSON and MediaType.APPLICATION_JSON_VALUE
My DTO class
public class CriarProdutoDTO {
#NotNull
#Size(min = 2)
#JsonProperty("nome_produto")
private final String nomeProduto;
#DecimalMin("0.1")
private final BigDecimal preco;
private final String descricao;
#NotNull
#Min(0)
#JsonProperty("quantidade_estoque")
private final Integer quantidadeEstoque;
#NotNull
#Min(1)
#JsonProperty("categoria_id")
private final Integer categoriaId;
public CriarProdutoDTO(String nomeProduto, BigDecimal preco, String descricao, Integer quantidadeEstoque, Integer categoriaId) {
this.nomeProduto = nomeProduto;
this.preco = preco;
this.descricao = descricao;
this.quantidadeEstoque = quantidadeEstoque;
this.categoriaId = categoriaId;
}
}
My current tests:
#ActiveProfiles("testes")
#ExtendWith(SpringExtension.class)
#SpringBootTest
#WebAppConfiguration
#AutoConfigureMockMvc(addFilters = false)
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)
public class ProdutoControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#Test
public void deveRetornarCreated_criacaoProdutoSucesso() throws Exception {
CriarProdutoDTO criarProdutoDTO = new CriarProdutoDTO("Nome", new BigDecimal("2.0"), "DESCRIÇÃO", 2, 1);
mockMvc.perform(MockMvcRequestBuilders.post("/api/produtos")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(objectMapper.writeValueAsString(criarProdutoDTO)))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isCreated());
}
}
My Controller:
#RestController
#RequestMapping(value = "/api/produtos")
public class ProdutoController {
#Autowired
private ProdutoService produtoService;
#Autowired
private CriarProdutoDtoToProdutoConverter produtoConverter;
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
public void cadastrar(#RequestBody #Valid CriarProdutoDTO produtoDTO) {
Produto novoProduto = produtoConverter.converter(produtoDTO);
produtoService.cadastrar(novoProduto);
}
}
try add Accept header to your request
Accept=application/json
I found out the problem.
The problem was occurring in Jackson's Serialization from my Data Transfer Object (DTO)
My DTO has an args-constructor and because of that i have to use the #JsonCreator to point the args constructor. What i didn't expect was that you must annotate all the constructor parameters with #JsonProperty as Jackson didn't know the exact order to instantiate the object from the construtor, that was my problem.
Another way is creating a bean for that, so you don't have to use the #JsonCreator
The solution:
#JsonCreator
public CriarProdutoDTO(
#JsonProperty("nome_produto") String nomeProduto, #JsonProperty("preco") BigDecimal preco,
#JsonProperty("descricao") String descricao, #JsonProperty("quantidade_estoque") Integer quantidadeEstoque,
#JsonProperty("categoria_id") Integer categoriaId) {
this.nomeProduto = nomeProduto;
this.preco = preco;
this.descricao = descricao;
this.quantidadeEstoque = quantidadeEstoque;
this.categoriaId = categoriaId;
}

#valid annotation sends exception in response to rest service

I am using custom validation in my rest web services.
#PUT
#Path("/{accountId}")
#Consumes({MediaType.APPLICATION_JSON})
public Response update(
#NotNull #ValidUUID #PathParam("accountId") UUID accUUID,
#NotNull #Valid Account changedAcc) {
synchronized (LOCK_MANAGER.getLock(accUUID)) {
return accHelper.update(this.getCurrentUser(), accUUID, changedAcc);
}
}
here is a glimpse at my Account class
#Table(keyspace = "Randomss", name = "accounts")
public class Account {
#PartitionKey
#Column(name = "id")
#JsonIgnore
private UUID id;
#Column(name = "acc_type")
#NotNull
#ValidString
#JsonIgnore
private String accType;
Now I send JSON data to this web service to update account,
but if I send some wrong json data
(e.g acc_type is expected as string and I send numeric data)
then it throws an exception.
How do I get it to send an error message instead of throwing an exception
(specifically, I want to send the error message)?
You need to write a javax.ws.rs.ext.Provider that implements an javax.ws.rs.ext.ExceptionMapper.
For example a generic ValidationExceptionMapper might look like:
#Provider
public class ValidationExceptionMapper
implements ExceptionMapper<ValidationException> {
public Response toResponse(ValidationException e) {
return Response.status(BAD_REQUEST).build();
}
}
You can choose a more appropriate response to return.

List of Pojo not getting converted into Json using #ResponseBody

I have controller class as below:
#RequestMapping(value = "/Reporting/FilterAsJson", method = RequestMethod.POST)
public #ResponseBody PagedQueryResult<GetEntitlementOverviewReportResult> filterAsJson(#ModelAttribute GetEntitleReportQuery query, HttpSession session)
{
getEntitlementOverviewFromSession(session).updateFromQuery(query, session);
return queryDispatcher.dispatch(query);}
The POJO class GetEntitlementOverviewReportResult is :
public class GetEntitlementOverviewReportResult
{
private Long id;
private String customerName;
private Long customerId;
private String customerNumber;
private String createdOn;
private String itemCreationDate;
private String licenseStatus;
private String licenseType;
private String licenseStatusCode;
private String licenseID;
private Long requestId;
private String licenseRootID;
private String customerNameCS;
private String customerNumberCS;
// <with getters and setters for the variables>
}
The problem is when all the fields in bean class is being set, proper Json is getting returned as a response. But when only first 6 fields are getting set, the response fails with 500 error in the debugger tool and doesn't return back to the calling ajax method. I get an "internal error" pop up in the browser. What am i missing here? Is is not possible to leave out the other fields whose values are not being fetched? I also tried using #JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) but it doesn't make any difference.

#QueryParam by default on all properties of #BeanParam in jersey 2

I want to use POJO as #BeanParam in jersey 2:
public class GetCompaniesRequest {
/** */
private static final long serialVersionUID = -3264610327213829140L;
private Long id;
private String name;
...//other parameters and getters/setters
}
#Path("/company")
public class CompanyResource {
#GET
public Response getCompanies(
#BeanParam final GetCompaniesRequest rq) {
...
}
}
There are many properties in GetCompaniesRequest and I want all them to be available as #QueryParameter. Can I achieve this without putting #QueryParam on every property?
You can inject the UriInfo and retrieve all the request parameters from it to a Map.
This will let you avoid injecting multiple query paramters with #QueryParam annotation.
#GET
public Response getCompanies(#Context UriInfo uris)
{
MultivaluedMap<String, String> allQueryParams = uris.getQueryParameters();
//Retrieve the id
long id = Long.parseLong(allQueryParams.getFirst("id"));
//Retrieve the name
String name = allQueryParams.getFirst("name");
//Keep retrieving other properties...
}
Otherwise, if you still need to use the #BeanParam , you will have to annotate each property in GetCompaniesRequest with #QueryParam:
public class GetCompaniesRequest implements MessageBody
{
#QueryParam("id")
private Long id;
#QueryParam("name")
private String name;
...
}

consuming response in jersey in java

I have a model class with an #XmlRootElement annotation. I want to know how I can receive it as a response in a function.
My model class:
#XmlRootElement
public class SurveyAnswers {
List<Long> Q_id;
Date date;
String SurveyType;
List<Answers> answers;
float versionid;
String mac;
String remarks;
// getter and setter here
}
I have previously used MultivaluedMap<String, String> for all my string based responses, but here it is a different structure to receive.
If your SurveyAnswers class is the only POST param then you can simply do like this:
#POST
#Path("/postpath")
#Consumes(MediaType.APPLICATION_JSON)
public Response handlePostRequest(SurveyAnswers surveyAnswers)
Follow this sample to learn more about consuming custom classes as the input params to jersey webservices:
http://www.mkyong.com/webservices/jax-rs/json-example-with-jersey-jackson/

Categories