javax.ws.rs.ProcessingException: RESTEASY003215: could not find writer for content-type application/json type: com.test.Request - jax-rs

I am using JAX-RS to develop an endpoint, I have the following class:
public class Request implements Serializable {
private String key;
private Content content;
private List<String> user;
}
And this is the endpoint that is configured:
#Path("/api/v1")
public class MyAPI {
#POST
#Path("/alerts")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
String postAlerts(Request request) throws WebApplicationException{
//Implementation
}
}
When I invoke the end point from client, I get following exception:
javax.ws.rs.ProcessingException: RESTEASY003215: could not find writer for content-type application/json type: com.test.Request
I have included all the dependencies mentioned in RESTEasy: Could not find writer for content-type application/json type. Specifically [resteasy-jaxrs, jaxrs-api, resteasy-jaxb-provider, resteasy-jackson2-provider] and I am able to verfiy that they are present in the classpath. Do I need provide some annotations on the 'Request' class which would create a writer for that for content-type application/json?

Related

After overriding the Application.getClasses() by a custom MessageBodyReader, methods on resource classes cannot be invoked

In a RESTEasy project running on Wildfly server, there is a resource class:
#Path("/company")
public class CompanyResource {
#Inject
CompanyService companyService;
#PUT
#Consumes(MediaType.APPLICATION_JSON)
public void update(Company company) {
companyService.update(company);
}
}
Initially the REST API configuration class just extends Application without any extra #override on the existing methods of Application class. An http request, http://localhost:8080/workcontext/company, with PUT as the http request method could work, meaning the CompanyResource.update() can be invoked successfully when receiving the aforementioned http request.
However, I then tried to add a custom MessageBodyReader<Company>:
public class CompanyReader implements MessageBodyReader<Company> {
#Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return true;
}
#Override
public Company readFrom(Class<Company> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
throws IOException, WebApplicationException {
try(JsonReader reader = Json.createReader(entityStream)) {
JsonObject companyJson = reader.readObject();
Company company = new Company();
company.setCompanyCode(companyJson.getString("companyCode"));
company.setName(companyJson.getString("name"));
company.setHeadquarter(Region.valueOf(companyJson.getString("headquarter")));
return company;
}
}
}
In order to make this custom MessageBodyReader<Company> work, I registered this class by overriding the Application.getClasses():
public class JaxRsConfiguration extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(CompanyReader.class);
return classes;
}
}
I expected that this MessageBodyReader<Company> could be invoked when sending the same http PUT request, but on the opposite the response is: RESTEASY003210: Could not find resource for full path: http://localhost:8080/workcontext/company
Question: How to make this custom MessageBodyReader work?
You should annotate you're CompanyReader with #Provider. In your application if you return any classes in Application.getClasses() or Application.getSingletons() then, per the spec, those are the only classes allowed to be used in your application.
If either getClasses or getSingletons returns a non-empty collection then only those classes or singletons returned MUST be included in the published JAX-RS application.

quarkus(vertx) how to get request body in a blocking way

What I've tried:
inject CurrentVertxRequest context and then get body from there
#Path("/map_event")
public class MapEventApi {
#Inject
CurrentVertxRequest reqContext;
#POST
#Consumes({ "application/json" })
#Produces({ "application/json" })
Response create(#Valid MapEvent mapEvent, #Context SecurityContext securityContext) throws Exception {
String body = reqContext.getCurrent().getBodyAsString();
...
}
}
but this will give a warning:
2022-01-25 18:22:08,854 WARN [null:-1] (executor-thread-0) BodyHandler in not enabled on this route: RoutingContext.getBodyAsString(...) in always be NULL
another try:
inject standard JaxRS HttpServletRequest context
#Context HttpServletRequest
will get this error:
org.jboss.resteasy.spi.LoggableFailure: RESTEASY003880: Unable to find contextual data of type: javax.servlet.http.HttpServletRequest
at org.jboss.resteasy.core.ContextParameterInjector$GenericDelegatingProxy.invoke(ContextParameterInjector.java:155)
at com.sun.proxy.$Proxy97.getInputStream(Unknown Source)
I guess it's because quarkus use vertx under the hood so injecting regular jaxrs context won't work since it's not the same thread.
According to #geoand proposiiton following solution worked for me:
#POST
#Consumes(MediaType.TEXT_PLAIN)
#Produces(MediaType.APPLICATION_JSON)
Response create(String body) throws Exception {
...
}
consuming the request as String was the trick → #Consumes(MediaType.TEXT_PLAIN)

Resteasy client : How to set Context Params

I'm using Resteasy client to run test cases for my Service. In application We set context Params in a session check filter,(which implements ContainerRequestFilter). I'm trying to set the same, in Resteasy client, using by adding a ClientRequestFilter implementation, but the property is not recognized, in the service call.
//Resteasy client calling logic
ResteasyClient resteasyClient = new ResteasyClientBuilder().build();
resteasyClient.register(new MyClientRequestFilter());
resteasyClient.target("http://localhost:" + port + "/myPath").request()
.post(Entity.json(authorization_reqParams)).readEntity(String.class));
//filter
public class MyClientRequestFilter implements ClientRequestFilter
{
#Override
public void filter(ClientRequestContext requestContext) throws IOException
{
requestContext.setProperty("CUSTOMER_ATTRIBUTE", "myCustomValue");
}
}
//Rest service method
#POST
#Path("/myPath")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response subpartner(Authorization_ReqParams authorizationReqParams, #Context HttpHeaders headers,
#Context HttpServletRequest request, #Context HttpServletResponse response)
{
String myAttribute= request.getAttribute("CUSTOMER_ATTRIBUTE");
//myAttribute is returned as null always
//additional logic
}
I'm able to set&get Header paramets with the same implementation, but Request param is always read as null.
How can I set the request context params ?
In MyClientRequestFilter you add a propery to the request object. What you really want is to send a header instead.
Try this instead:
#Override
public void filter(ClientRequestContext requestContext) {
MultivaluedMap<String, Object> headers = requestContext.getHeaders();
headers.add("CUSTOMER_ATTRIBUTE", "myCustomValue");
}
And read it like this:
#POST
#Path("/myPath")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response subpartner(Authorization_ReqParams authorizationReqParams, #Context HttpHeaders headers,
#Context HttpServletRequest request, #Context HttpServletResponse response)
{
String myAttribute= headers.getRequestHeader("CUSTOMER_ATTRIBUTE");
//additional logic
}

Restlet JAX-RS deserializes into ArrayList<LinkedHashMap>

I started using the Restlet JAX-RS extension, but I'm finding that lists get deserialized into an ArrayList of LinkedHashMap instead of the class.
#Path("MyService")
public class MyService {
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("myTestMethod")
public String testIt(final List<MyTestClass> myList) {
return "hello";
}
}
public class MyTestClass {
#JsonProperty("name")
private String name;
}
Any ideas on how to have Jackson deserialize into List<MyTestClass>? It works fine when I don't use the JAX-RS extension, but I need this to port over some existing web services.
Thanks for any help.
==Update==
Sending data like this:
import requests
import json
url = "http://localhost:8182/MyService/myTestMethod"
headers = {
"Content-Type": "application/json",
"Accept": "application/json"
}
request_object = [{"name": "joe"}, {"name": "bill"}]
params = json.dumps(request_object)
response = requests.post(url, data=params, headers=headers)

Call another rest api from my server in Spring-Boot

I want to call another web-api from my backend on a specific request of user. For example, I want to call Google FCM send message api to send a message to a specific user on an event.
Does Retrofit have any method to achieve this? If not, how I can do that?
This website has some nice examples for using spring's RestTemplate.
Here is a code example of how it can work to get a simple object:
private static void getEmployees()
{
final String uri = "http://localhost:8080/springrestexample/employees.xml";
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri, String.class);
System.out.println(result);
}
Modern Spring 5+ answer using WebClient instead of RestTemplate.
Configure WebClient for a specific web-service or resource as a bean (additional properties can be configured).
#Bean
public WebClient localApiClient() {
return WebClient.create("http://localhost:8080/api/v3");
}
Inject and use the bean from your service(s).
#Service
public class UserService {
private static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(3);
private final WebClient localApiClient;
#Autowired
public UserService(WebClient localApiClient) {
this.localApiClient = localApiClient;
}
public User getUser(long id) {
return localApiClient
.get()
.uri("/users/" + id)
.retrieve()
.bodyToMono(User.class)
.block(REQUEST_TIMEOUT);
}
}
Instead of String you are trying to get custom POJO object details as output by calling another API/URI, try the this solution. I hope it will be clear and helpful for how to use RestTemplate also,
In Spring Boot, first we need to create Bean for RestTemplate under the #Configuration annotated class. You can even write a separate class and annotate with #Configuration like below.
#Configuration
public class RestTemplateConfig {
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
Then, you have to define RestTemplate with #Autowired or #Injected under your service/Controller, whereever you are trying to use RestTemplate. Use the below code,
#Autowired
private RestTemplate restTemplate;
Now, will see the part of how to call another api from my application using above created RestTemplate. For this we can use multiple methods like execute(), getForEntity(), getForObject() and etc. Here I am placing the code with example of execute(). I have even tried other two, I faced problem of converting returned LinkedHashMap into expected POJO object. The below, execute() method solved my problem.
ResponseEntity<List<POJO>> responseEntity = restTemplate.exchange(
URL,
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<POJO>>() {
});
List<POJO> pojoObjList = responseEntity.getBody();
Happy Coding :)
Create Bean for Rest Template to auto wiring the Rest Template object.
#SpringBootApplication
public class ChatAppApplication {
#Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ChatAppApplication.class, args);
}
}
Consume the GET/POST API by using RestTemplate - exchange() method. Below is for the post api which is defined in the controller.
#RequestMapping(value = "/postdata",method = RequestMethod.POST)
public String PostData(){
return "{\n" +
" \"value\":\"4\",\n" +
" \"name\":\"David\"\n" +
"}";
}
#RequestMapping(value = "/post")
public String getPostResponse(){
HttpHeaders headers=new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity=new HttpEntity<String>(headers);
return restTemplate.exchange("http://localhost:8080/postdata",HttpMethod.POST,entity,String.class).getBody();
}
Refer this tutorial[1]
[1] https://www.tutorialspoint.com/spring_boot/spring_boot_rest_template.htm
As has been mentioned in the various answers here, WebClient is now the recommended route.
You can start by configuring a WebClient builder:
#Bean
public WebClient.Builder getWebClientBuilder(){
return WebClient.builder();
}
Then inject the bean and you can consume an API as follows:
#Autowired
private WebClient.Builder webClientBuilder;
Product product = webClientBuilder.build()
.get()
.uri("http://localhost:8080/api/products")
.retrieve()
.bodyToMono(Product.class)
.block();
Does Retrofit have any method to achieve this? If not, how I can do that?
YES
Retrofit is type-safe REST client for Android and Java. Retrofit turns your HTTP API into a Java interface.
For more information refer the following link
https://howtodoinjava.com/retrofit2/retrofit2-beginner-tutorial
In this case need download whit my API, files hosted in other server.
In my case, don't need use a HTTP client to download the file in a external URL, I combined several answers and methods worked in previous code for files that were in my local server.
My code is:
#GetMapping(value = "/download/file/pdf/", produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<Resource> downloadFilePdf() throws IOException {
String url = "http://www.orimi.com/pdf-test.pdf";
RestTemplate restTemplate = new RestTemplate();
byte[] byteContent = restTemplate.getForObject(url, String.class).getBytes(StandardCharsets.ISO_8859_1);
InputStream resourceInputStream = new ByteArrayInputStream(byteContent);
return ResponseEntity.ok()
.header("Content-disposition", "attachment; filename=" + "pdf-with-my-API_pdf-test.pdf")
.contentType(MediaType.parseMediaType("application/pdf;"))
.contentLength(byteContent.length)
.body(new InputStreamResource(resourceInputStream));
}
and it works with HTTP and HTTPS urls!
Since the question explicitly tags spring-boot, it worth noting that recent versions already ship a pre-configured instance of a builder for WebClient, thus you can directly inject it inside your service constructor without the needing to define a custom bean.
#Service
public class ClientService {
private final WebClient webClient;
public ClientService(WebClient.Builder webClientBuilder) {
webClient = webClientBuilder
.baseUrl("https://your.api.com")
}
//Add all the API call methods you need leveraging webClient instance
}
https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/boot-features-webclient.html
Simplest way I have found is to:
Create an annotated interface (or have it generated from somehing like OpenAPI)
Give that interface to Spring RestTemplate Client
The Spring RestTemplate Client will parse the annotations on the interface and give you a type safe client, a proxy-instance. Any invocation on the methods will be seamlessly translated to rest-calls.
final MyApiInterface myClient = SpringRestTemplateClientBuilder
.create(MyApiInterface.class)
.setUrl(this.getMockUrl())
.setRestTemplate(restTemplate) // Optional
.setHeader("header-name", "the value") // Optional
.setHeaders(HttpHeaders) // Optional
.build();
And a rest call is made by inoking methods, like:
final ResponseEntity<MyDTO> response = myClient.getMyDto();