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

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;
...
}

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;
}

Make an HTTP request with body names set in #JsonProperty

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;
...
}

Deserialize two different JSON representations into one object

I have Java class like
#Data
public class Comment {
private Integer id; // should be used anyhow
private Long refId; // for internal purpose -> not be serialized
private String text; // should be used in QuickComment
private String patch; // should be included in PatchComment ONLY
private String status; // should be included in StatusComment ONLY
}
and I have
#Data
public class Response{
private Comment statusComment;
private Comment patchComment;
}
I thought about using JsonView like
public class Views{
public interface StatusComment{}
public interface PatchComment{}
}
and apply them to the inital class
#Data
public class Comment {
#JsonView({Views.StatusComment.class, Views.PatchComment.class})
private Integer id; // should be used anyhow
private Long refId; // for internal purpose -> not be serialized
#JsonView({Views.StatusComment.class, Views.PatchComment.class})
private String text; // should be used anyhow
#JsonView(Views.PatchComment.class)
private String patch; // should be included in PatchComment ONLY
#JsonView(Views.StatusComment.class)
private String status; // should be included in StatusComment ONLY
}
and the Response
#Data
public class Response{
#JsonView(Views.StatusComment.class)
private Comment statusComment;
#JsonView(Views.PatchComment.class)
private Comment patchComment;
}
But somehow it fails completely. It fails completly, ie. nothing is filtered. Is it problem with Lombok. Or is it defined incorrect?
How do you serialize your objects? Are you using Spring? Are you using the ObjectMapper directly?
If you're using Spring then what you need to do is annotate method of your controllers with #JsonView(Views.StatusComment.class) or #JsonView(Views.PatchComment.class) like:
For reading GET endpoints
#JsonView(Views.StatusComment.class)
#RequestMapping("/comments/{id}")
public Comment getStatusComments(#PathVariable int id) {
return statusService.getStatuscommentById(id);
}
For writing:
#RequestMapping(value = "/persons", consumes = APPLICATION_JSON_VALUE, method = RequestMethod.POST)
public Comment saveStatusComment(#JsonView(View.StatusComment.class) #RequestBody Comment c) {
return statusService.saveStatusComment(c);
}
If you're using the ObjectMapper directly, then what you need to do is specify the used View:
When writing:
ObjectMapper mapper = new ObjectMapper();
String result = mapper
.writerWithView(Views.StatusComment.class)
.writeValueAsString(comment);
When reading:
ObjectMapper mapper = new ObjectMapper();
Comment comment = mapper
.readerWithView(Views.StatusComment.class)
.forType(Comment.class)
.readValue(json);

#JsonView with Spring PagedResources

I've a pojo exposed with Rest Controller. I need to hide some properties for one GET request, so I decided to use jackson's annotation #JsonView. I can't find any way to made it with #JsonView and PagedResources.
Here is my pojo :
public class Pojo {
interface RestrictedPojo {}
interface AllPojo extends RestrictedPojo {}
#Id
#JsonView(RestrictedPojo.class)
private String identifier;
#JsonView(AllPojo.class)
private String someproperty;
/**
* Property I want to hide
*/
#JsonView(RestrictedPojo.class)
private String someHiddenProperty;
}
Here is my Controller :
#RepositoryRestController
#RequestMapping(value = "/pojo")
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class PojoController {
private final PojoService pojoService;
private final IdentityUtils identityUtils;
private final PagedResourcesAssembler<Pojo> pagedResourcesAssembler;
#PreAuthorize("hasRole('SOME_ROLE')")
#GetMapping
#JsonView(Pojo.RestrictedPojo.class)
public ResponseEntity<PagedResources<Resource<Pojo>>> getAllRestrictedPojos(final Pageable pageable) {
final Page<Pojo> allPojo = pojoService.getAllRestrictedPojos(pageable);
final PagedResources<Resource<Pojo>> resources = pagedResourcesAssembler.toResource(allPojo );
return ResponseEntity.ok(resources);
}
#PreAuthorize("hasRole('SOME_ROLE')")
#GetMapping
#JsonView(Pojo.AllPojo.class)
public ResponseEntity<PagedResources<Resource<Pojo>>> getAllPojos(final Pageable pageable) {
final Page<Pojo> allPojo = pojoService.getAllRestrictedPojos(pageable);
final PagedResources<Resource<Pojo>> resources = pagedResourcesAssembler.toResource(allPojo );
return ResponseEntity.ok(resources);
}
}
I didn't wrote specific config, it's a basic spring boot app.
Can anyone help ?
Thanks

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.

Categories