I am trying to test a Jax-rs resource by following this https://jersey.java.net/documentation/latest/test-framework.html,
and I am using container jersey-test-framework-provider-jdk-http
I can assert status code. However, when I try to readEntity, I get exception:
javax.ws.rs.ProcessingException: Unable to find a MessageBodyReader of content-type application/json and type class java.lang.String
at org.jboss.resteasy.core.interception.ClientReaderInterceptorContext.throwReaderNotFound(ClientReaderInterceptorContext.java:39)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.getReader(AbstractReaderInterceptorContext.java:73)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:50)
at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(ClientResponse.java:248)
at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readEntity(ClientResponse.java:181)
at org.jboss.resteasy.specimpl.BuiltResponse.readEntity(BuiltResponse.java:217)
My Resource Class:
#Path("/")
public class SampleResource {
#GET
#Path("/health")
#Produces(MediaType.APPLICATION_JSON)
public String getServiceStatus() {
return "{\"Status\": \"OK\"}";
}
}
My Test Class:
public class TestSampleResource extends JerseyTest {
#Override
protected Application configure() {
return new ResourceConfig(SampleResource.class);
}
#Test
public void testHealthEndpoint() {
Response healthResponse = target("health").request(MediaType.APPLICATION_JSON).get();
Assert.assertEquals(200, healthResponse.getstatus()); // works
String body = healthResponse.readEntity(String.class);
Assert.assertEquals("{\"Status\": \"OK\"}", body);
}
}
Can anyone please help?
The problem comes from having both Jersey and RestEasy client on the classpath. When you call target() on the JerseyTest, the WebTarget is obtained from a Client that is built by calling ClientBuilder.newClient().
The ClientBuilder is a standard JAX-RS API, and it is implemented first to search for an implementation of ClientBuilder through the META-INF/services files, looking for a file named javax.ws.rs.client.ClientBuilder, whose content is the name of an implementation of the ClientBuilder. If no such file is found, it defaults to looking for JerseyClientBuilder.
jersey-client has no such file META-INF/services/javax.ws.rs.core.ClientBuilder because it's ClientBuilder is the default for JAX-RS client. If you look in your resteasy-client jar, you will see the it does have that file. And if you look in the contents of that file, you will see the ResteasyClientBuilder as the implementation.
So even though you are using Jersey's test framework, the Client being used, is RESTeasy's implementation. And I guess all the standard configurations with entity providers never gets configured. Conversion between String and application/json is one of those standard providers you need in your case.
I would say just explicitly use Jersey client implementation. You will no longer be able to call target on the JerseyTest. You will need to explicitly create the client
#Test
public void dotest() {
final Client client = new JerseyClientBuilder().build();
WebTarget target = client.target("http://localhost:9998");
final Response response = target.path("health").request().get();
final String json = response.readEntity(String.class);
}
The default base path for JerseyTest is http://localhost:9998, so I explicitly create the WebTarget with that.
Note that I said the String to/from application/json is supported by standard providers. So if you will only be serializing Strings, then you don't need anything else. If you want POJO serialization support for the Jersey client (and server side), you should add the following
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey2.version}</version>
<scope>test</scope>
</dependency>
I suspect the json parser in your test is being misguided by the presence of curly braces. Basically it thinks you are returning a json object, not a json string. Try returning "Status:OK"
As the exception says you are missing a MessageBodyReader for content-type application/json. Do you have JacksonJsonProvider on your classpath? It can be added as a dependency to jackson-jaxrs-json-provider:
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.7.3</version>
</dependency>
Then register the JacksonJsonProvider in your test application:
#Override
protected Application configure() {
return new ResourceConfig(SampleResource.class, JacksonJsonProvider.class);
}
Related
I need to send HTTP requests from my Quarkus application. Following this guide, I have this RestClient:
#Path("/v1")
#RegisterRestClient
public interface CountriesService {
#GET
#Path("/name/{name}")
Set<Country> getByName(#PathParam String name);
}
In the Path annotation, I can configure the path. But the domain/url to call is defined in a configuration file, according to this paragraph.
# Your configuration properties
org.acme.rest.client.CountriesService/mp-rest/url=https://restcountries.eu/rest #
org.acme.rest.client.CountriesService/mp-rest/scope=javax.inject.Singleton #
In my case, I need this URL to be defined programmatically at runtime, as I receive it as a callback URL.
Is there a way to do that?
Quarkus Rest Client, and Quarkus Rest Client Reactive, implement the MicroProfile Rest specification and as such allow creating client stubs with RestClientBuilder programmatically, e.g.:
public class SomeService {
public Response doWorkAgainstApi(URI apiUri, ApiModel apiModel) {
RemoteApi remoteApi = RestClientBuilder.newBuilder()
.baseUri(apiUri)
.build(RemoteApi.class);
return remoteApi.execute(apiModel);
}
}
See https://download.eclipse.org/microprofile/microprofile-rest-client-2.0/microprofile-rest-client-spec-2.0.html#_sample_builder_usage
You cannot achieve this with client created with the #RegisterRestClient annotation
I am implementing a rest-full Web service with Jersey which accepts http request from client in json form.
On fortify scan I am getting critical issue :-"mass assignment insecure binder configuration".
I want to bind the json values in htttp request to the model class in my server side code and since it is a small module I want to avoid using Spring MVC framework.
Below is my code snippet which is working fine but I need to map json request to model class below without using Spring MVC.
#POST
#Path("/TimRestService")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public Response crunchifyREST**(JsonObject model**, #Context HttpServletRequest request) {
System.out.println(model);
return Response.status(200).entity(model).build();
}
This is the model class :-
public class ActivateService {
public String mWalletToken;
public String topMerchantEMPID;
public String serviceCategory;
}
I checked these links , however the answer is more specific to Spring MVC fmwrk:
What is the solution for Mass Assignment: Insecure Binder Configuration Vulnerability?
How to fix Mass Assignment: Insecure Binder Configuration (API Abuse, Structural) in java
This can be implemeted via Jacksonson . Jackson is one of the best JSON Providers/parsers and can be used with Jersey in Rest implemetation.The REST services will produce and consume JSON and the JSON serialization and de-serialization happens automatically behind the scenes
Create View class as :
public class View {
public static class Editable {}
public static class Viewable extends Editable {}
public static class Internal extends Viewable {}
}
Create Model class as :
#JsonIgnoreProperties(ignoreUnknown = true)
#XmlRootElement(name = "activateService")
public class ActivateService implements Serializable {
#JsonView(View.Editable.class)
public String mWalletToken;
#JsonView(View.Editable.class)
public String topMerchantEMPID;
#JsonView(View.Editable.class)
public String serviceCategory;
}
and the Rest -full web service method :
#POST
#Path("/TimRestService")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public Response crunchifyREST(#JsonView(View.Editable.class) final ActivateService model, #Context HttpServletRequest request) {
In JAX-RS, if one model(either request or response) is annotated with #JsonView(View.Editable.class), in our case add method, Jackson will only serialize or deserialize fields that are annotated with #JsonView(View.Editable.class).
In our case, client can only pass editableField, if client pass any other fields, server will just silently ignore them.
Use below dependencies in pom.xml
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-
databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.sun.jersey/jersey-json -->
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.19.4</version>
</dependency>
Source :- https://www.owasp.org/index.php/Mass_Assignment_Cheat_Sheet
and http://lifelongprogrammer.blogspot.com/2015/09/using-jackson-view-to-protect-mass-assignment.html
it also works just by placing this in the pojo or model class
#JsonIgnoreProperties(ignoreUnknown=true)
public class ActivateService {
[...]
}
resource:
https://stackoverflow.com/a/39013609/8782229
I have a project with spring boot and I want to use swagger2 to document my json web services.
I have this configuration :
#Configuration
#EnableSwagger2
public class Swagger2Config {
#Bean
public Docket welcomeMessageApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("My API")
.description("Lorem Ipsum is simply dummy text of ...")
.termsOfServiceUrl("an url")
.contact("contact")
.license("")
.licenseUrl("")
.version("2.0")
.build();
}
To read the documentation, I use this link : http://localhost:9081/v2/api-docs
In the swagger UI, it works fine. But when I try this link directly in my browser, I have this error :
With Firebug, I see that it accept XML content instead of JSON content.
How can I modify swagger configuration to accept JSON content ?
You meet the problem because of the Spring MVC default get the server to render XML instead of JSON in a browser.
The official document say:
To get the server to render XML instead of JSON you might have to send an Accept: text/xml header (or use a browser).
So all you need to do is make the server render JSON in browser.
When you deep into the request in browser you'll see the Request Header:
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
And if you debug into the spring boot, you will see the spring mvc will default delegate HttpMessageConverters include MappingJackson2XmlHttpMessageConverter and MappingJackson2HttpMessageConverter.
The MappingJackson2HttpMessageConverter is to render json and MappingJackson2XmlHttpMessageConverter is to render xml.
They both have a field supportedMediaTypes which means what mediatypes are supported.
The value of supportedMediaTypes in MappingJackson2HttpMessageConverter is:
The value of supportedMediaTypes in MappingJackson2XmlHttpMessageConverter is:
There is a 'text/xml;charset=UTF-8' in MappingJackson2XmlHttpMessageConverter.This is why browser render xml instend of json.
So you need add a custom MappingJackson2XmlHttpMessageConverter which support 'text/xml', for example :
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
List<MediaType> list = new ArrayList<>();
list.add(MediaType.APPLICATION_JSON_UTF8);
list.add(new MediaType("text", "html", Charset.forName("UTF-8")));
list.add(new MediaType("application", "*+json", Charset.forName("UTF-8")));
converter.setSupportedMediaTypes(list);
converters.add(converter);
}
}
Try this and browser will render JSON instead of XML in browser, and all things right!
What worked for me was updating the Maven dependencies for springfox-swagger2 and springfox-swagger-ui.
The newest working combination for me was:
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
I had a similar issue that the Swagger API returns XML when I tried to read it with a web-browser.
I resolved it with a different approach.
If you try calling the endpoint with some other tools, such as PostMan or Curl command tool, it would be return JSON correctly.
It looks like it happened in a web-browser only.
If you have Jackson XML extension in pom.xml, you may try this approach.
But if you need to return XML data as a response, please find out another solution.
This is my approach, add below Configuration which extends the WebMvcConfigurationSupport class to the project source code.
#Configuration
public class MessageConverterConfiguration extends WebMvcConfigurationSupport {
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
HttpMessageConverter<?> target = null;
for (HttpMessageConverter<?> converter : converters) {
if (converter.getClass() == MappingJackson2XmlHttpMessageConverter.class) {
target = converter;
}
}
if (target != null) {
converters.remove(target);
}
}
}
For regular GET type request, web-browser sends a request data to the endpoint, including the 'Accept' key in request header, having 'xml' value in it.
Besides, on the server-side, even if the response data type is JSON, and even if there's JSON message converter specified, Spring picks up the XML message converter first, if it is compatible with the JSON data.
On top of the above, the selection process is based on the order of 'Accept' value from request.
You may debug the AbstractMessageConverterMethodProcessor, especially the writeWithMessageConverters method during the response
process.
In the selecting process, Spring picks up a proper converter based on the registered message converter list which is initialized on the process of the system initialization by the WebMvcConfigurationSupport.
The WebMvcConfigurationSupport provides extendMessageConverters to extend or modify the converter list.
So I tried to remove that special XML message converter from the list and it works.
Although above solution worked for me, you may not use this approach.
Please find out another solution if you need to return XML data as a response.
I have a field annotated with #NotNull, when I pass JSON to my Rest resource that is missing that field, it just carries on.
From my understanding of the documentation, I don't need anything else than what I've got to do simple validation.
As far as I can tell, I have everything in place, not sure what I'm missing. Using Java 8, Jersey 2.8, maven tomcat7 plugin.
#Path("/trade")
public class TradeEndpoint {
#Autowired
private OrderFillService orderFillService;
#POST
#Path("limit")
#Consumes({ MediaType.APPLICATION_JSON })
public void limitOrder(#Valid LimitOrderModel limitOrder) {
placeTrade(limitOrder);
}
...
}
public class LimitOrderModel {
#NotNull
private String symbol;
...
}
I set the property to send errors back to the client:
public Application () {
packages("com.foo.web.endpoint");
register(JacksonFeature.class);
property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, "true");
}
I have a dependency on jersey-bean-validation:
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<version>2.21</version>
</dependency>
Posting this JSON works (placeTrade() is executed):
{
"limitPrice":"60",
"side":"SELL",
"quantity":"100"
}
Found the problem. I'm on Jersey 2.8 (makes life with jackson easier) but I was depending on jersey-bean-validation 2.21. Dropping down to jersey-bean-validation 2.8 works a treat.
I am trying to write a Custom CXF Interceptor to do some validations on SOAP request to a web service. Based on the validation results, I want to block the request to web service and return the response with some modified parameters.
For this, I have written custom CXF ininterceptor extending from AbstractPhaseInterceptor, to run in phase USER_LOGICAL, which does validations, but I am not able to stop the subsequent call to web service and also not able to pass the Custom Response object(Custom Response object type is same as web service return type). How can I do this using interceptors?
I did some research upon the tip from nadirsaghar and I found it to be the cleanes solution available. Using message.getExchange() in JAX-WS is a complete pain, since you have to setup a conduit and fill the response message yourself...
So better do it this way, using HttpServletResponse. - You need to have the java servlet-api.jar on your Path. If you're developing without maven, just link it from your webserver (e.g. tomcat) directory, but exlude it from deployment.
<!-- With Maven add the following dependency -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<!-- The version should match your WebService version e.g. 3.0 for JDK7-->
<version>2.5</version>
<scope>provided</scope>
</dependency>
With scope provided it will not be deployed and is just available so you can access the HttpServletResponse class.
Your Handler Code:
#Override
public void handleMessage( final Message message ) throws Fault
{
if( shouldBlockMessage( message ) )
{
message.getInterceptorChain().abort();
final HttpServletResponse response = (HttpServletResponse)message.get( AbstractHTTPDestination.HTTP_RESPONSE );
// To redirect a user to a different Page
response.setStatus( HttpServletResponse.SC_MOVED_TEMPORARILY );
response.setHeader( "Location", "http://www.bla.blubb/redirectPage" );
// Other possibility if a User provides faulty login data
response.setStatus( HttpServletResponse.SC_FORBIDDEN );
}
}
You can abort execution of the interceptorChain including the webservice using abort method
public void handleMessage(SoapMessage message) {
InterceptorChain chain = message.getInterceptorChain();
chain.abort();
}
Something like this, there is no need to play with Interceptor chain.
public void handleMessage(Message message) {
//your logic
Response response = Response.status(Status.UNAUTHORIZED).type(MediaType.APPLICATION_JSON).build();
message.getExchange().put(Response.class, response);
}