I work on small test project to check how Spring Reactive Web Applications actually works with MongoDB.
I follow the manual from https://docs.spring.io/spring/docs/5.0.0.M4/spring-framework-reference/html/web-reactive.html
and it states that I can process POST request in controller like:
#PostMapping("/person")
Mono<Void> create(#RequestBody Publisher<Person> personStream) {
return this.repository.save(personStream).then();
}
Though this seems not works. Here the controller I implemented:
https://github.com/pavelmorozov/reactor-poc/blob/master/src/main/java/com/springreactive/poc/controller/BanquetHallController.java
it have just one POST mapping and it is very simple:
#PostMapping("/BanquetHall")
Mono<Void> create(#RequestBody Publisher<BanquetHall> banquetHallStream) {
return banquetHallRepository.insert(banquetHallStream).then();
}
It is called each time I issue a POST with curl:
curl -v -XPOST -H "Content-type: application/json" -d '{"name":"BH22"}' 'http://localhost:8080/BanquetHall'
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /BanquetHall HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.47.0
> Accept: */*
> Content-type: application/json
> Content-Length: 15
>
* upload completely sent off: 15 out of 15 bytes
< HTTP/1.1 200 OK
< content-length: 0
<
* Connection #0 to host localhost left intact
And I see new objects stored in mongodb, but they not contain data. To debug I build simple subscriber, to see the data actually passed as request body to controller:
Subscriber s = new Subscriber() {
#Override
public void onSubscribe(Subscription s) {
logger.info("Argument: "+s.toString());
}
#Override
public void onNext(Object t) {
logger.info("Argument: "+t.toString());
}
#Override
public void onError(Throwable t) {
logger.info("Argument: "+t.toString());
}
#Override
public void onComplete() {
logger.info("Complete! ");
}
};
banquetHallStream.subscribe(s);
and now I see after subscription onError method called. The Throwable states body missing:
Here error string:
Request body is missing: reactor.core.publisher.Mono<java.lang.Void> com.springreactive.poc.controller.BanquetHallController.create(org.reactivestreams.Publisher<com.springreactive.poc.domain.BanquetHall>)
Why request body is empty?
Also good to know: As I new with all this reactive stuff, could it be some better approach to debug Publisher/Subscriber without manual implementing Subscriber?
Update I updated POST handler method description and it passes request body as String object:
Mono<Void> create(#RequestBody String banquetHallStream)
Then this is not a "Reactive", right? String is not reactive, as Publisher should be...
I had exact the same issue and was able to solve it by putting #ResponseStatus on method. Below is how method controller looks like:
#ResponseStatus(HttpStatus.CREATED)
#PostMapping(value = "/bulk", consumes = APPLICATION_STREAM_JSON_VALUE)
public Mono<Void> bulkInsert(#RequestBody Flux<Quote> quotes) {
return quoteReactiveRepository.insert(quotes).then();
}
I'm doing the request to that endpoint using WebClient:
webClient.post()
.uri("/quotes/bulk")
.contentType(MediaType.APPLICATION_STREAM_JSON)
.body(flux(), Quote.class)
.retrieve()
.bodyToMono(Void.class).block();
tested with: org.springframework.boot:spring-boot-starter-webflux:2.1.0.RELEASE
Related
I've created a route to allow me to forward a REST call. Everything is going well, except I cannot modify the HTTP headers of the response (actually I can't even get them untouched on the response).
// My processor
private void remplacerLiensDansHeader(final Exchange exchange, final String rootUrlPivotJoram, final String rootUrlRemplacement) {
// That is OK, I get the values just fine
ArrayList<String> oldLinks = exchange.getIn().getHeader(HEADER_LINK, ArrayList.class);
// This is also OK
final List<String> newLinks = anciensLiens.stream().map(lien -> lien.replace(rootUrlPivotJoram, rootUrlRemplacement)).collect(toList());
// No error, but apparently doesn't work
exchange.getMessage().setHeader(HEADER_LINK, newLinks);
exchange.getMessage().setHeader("test", "test");
}
// Route configuration
#Override
public void configure() throws Exception {
this.from(RestRouteBuilder.endPoint(createProducerJoramConfiguration))
.setExchangePattern(InOut)
.removeHeader(Exchange.HTTP_URI)
.toD(createProducerJoramConfiguration.getUrlDestination())
.setHeader("test", () -> "test") // that doesn't work either
.process(createProducerJoramConfiguration.getProcessor()); // this is the processor with the code above
}
This is the response I get (note that the response code is 200 and I think it's weird as the original is 201)
curl -v -XPost --user "xxx:yyyy" http://localhost:10015/zzzz/webservices/xxxxx
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 10015 (#0)
* Server auth using Basic with user 'xxx'
> Post /zzzzz/webservices/eeee HTTP/1.1
> Host: localhost:10015
> Authorization: Basic pppppppppppppppppp==
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 200
< Date: Tue, 31 Aug 2021 11:17:49 GMT
< Content-Type: application/octet-stream
< Content-Length: 0
<
* Connection #0 to host localhost left intact
Two things I've noticed:
if I add a body in the processor, then the body is present in the response,
if I remove the processor, the headers present in the "original response" are not present either.
I don't know what headers you exactly lose, but be aware that the Camel HTTP component has a default header filter (as lots of Camel components have).
If you don't specify your own HeaderFilterStrategy, the default HttpHeaderFilterStrategy is used.
This default filters the following headers:
content-length
content-type
host
cache-control
connection
date
pragma
trailer
transfer-encoding
upgrade
via
warning
Camel*
org.apache.camel*
With this filter, Camel wants to avoid that old HTTP headers are still present on outgoing requests (probably with wrong data).
The filtering of Camel headers is just to remove Camel specific stuff that is not relevant for HTTP.
Actually, the problem was the cxfrs component.
We manage to find an answer here : see : Response to REST client from Camel CXFRS route?
Here is the final solution.
Thanks to everyone that looked or answer, I hope that'll help someone else.
public class ModificationHeaderLinkProcessor implements Processor {
private static final String HEADER_LINK = "Link";
#Override
public void process(final Exchange exchange) {
List<String> newLinks= getNewLinks(exchange, oldUrl, newUrl);
ResponseBuilder builder = createHttpResponse(exchange);
builder.header(HEADER_LINK, newLinks);
exchange.getIn().setBody(builder.build());
}
private List<String> getNewLinks(final Exchange exchange, final String oldUrl, final String newUrl) {
ArrayList<String> oldLinks= exchange.getIn().getHeader(HEADER_LINK, ArrayList.class);
return oldLinks.stream().map(link-> link.replace(oldUrl, newUrl)).collect(toList());
}
private ResponseBuilder createHttpResponse(final Exchange exchange) {
ResponseBuilder builder = Response.status(getHttpStatusCode(exchange))
.entity(exchange.getIn().getBody());
clearUselessHeader(exchange);
exchange.getIn().getHeaders().forEach(builder::header);
return builder;
}
private void clearUselessHeader(final Exchange exchange) {
exchange.getIn().removeHeader(HEADER_LINK);
exchange.getIn().removeHeaders("Camel*");
}
private Integer getHttpStatusCode(final Exchange exchange) {
return exchange.getIn().getHeader(exchange.HTTP_RESPONSE_CODE, Integer.class);
}
private final String getPropertiesValue(CamelContext camelContext, String key) {
return camelContext.getPropertiesComponent().resolveProperty(key).orElseThrow();
}
}
When we add the Micronaut security all the URLs are protected. while using the #Secured(SecurityRule.IS_ANONYMOUS) annotation still facing the 401 exception
public interface ISubCategoryOperation {
#Get("/{categoryId}/sub-category")
#Secured(SecurityRule.IS_ANONYMOUS)
Maybe<?> get(#NotNull String categoryId);
}
The interface method is denoted with #Secured(SecurityRule.IS_ANONYMOUS) but still not able to go to the controller method
Http client
#Client(id="feteBirdProduct", path = "/category")
public interface ISubCategoryClient extends ISubCategoryOperation{
}
Controller
#Controller("/category/{categoryId}/sub-category")
public class SubCategoryController implements ISubCategoryOperation {
#Override
public Maybe<?> get(#PathVariable String categoryId) {
return Maybe.Just(null);
}
Curl
curl -X GET "http://localhost:8080/api/v1/category/60236833af7a1d49478d2bef/sub-category" -H "accept: application/json"
Http client logs
15:17:18.504 [default-nioEventLoopGroup-1-6] DEBUG i.m.h.client.netty.DefaultHttpClient - Sending HTTP GET to http://localhost:8081/category/60236833af7a1d49478d2bef/sub-category
15:17:18.535 [default-nioEventLoopGroup-1-6] DEBUG i.m.h.client.netty.DefaultHttpClient - Received response 401 from http://localhost:8081/category/60236833af7a1d49478d2bef/sub-category
I'm testing some API with restassured. My call to restassured looks (more-or less) like this:
public void doRequest() {
XMLRequestBindingClass body = new XMLBindingClass(/*some paramenters*/);
RequestSpecification request = given()
.config(getDefaultConfig())
.contentType(ContentType.XML)
.header(AUTH_HEADER) // "SOAPAction","http://some.soap.action"
.header(ACTION_HEADER) // "Authorization", "s0meHash"
.body(body, ObjectMapperType.JAXB);
XMLResponseBindingClass response = getServiceResponse(request, URL, timeout)
.as(XMLResponseBindingClass.class);
}
protected Response getServiceResponse(RequestSpecification request, String url, long timeout) {
if (isDebugEnabled) {
request.log().all();
}
Response response = request.when().post(url);
if (isDebugEnabled) {
response.prettyPeek();
}
ValidatableResponse validatableResponse = response
.then().statusCode(200);
if (checkTime && timeout != 0) {
validatableResponse.time(lessThanOrEqualTo(timeout));
}
return validatableResponse.extract().response();
}
protected static RestAssuredConfig getDefaultConfig() {
RestAssuredConfig config = RestAssuredConfig
.config()
.sslConfig(SSLConfig.sslConfig().relaxedHTTPSValidation())
.encoderConfig(EncoderConfig.encoderConfig().defaultContentCharset(Charset.forName("UTF-8")))
.logConfig(LogConfig.logConfig()
.enablePrettyPrinting(true))
.xmlConfig(XmlConfig.xmlConfig()
.with().namespaceAware(true)
.with().allowDocTypeDeclaration(true));
if (!isDebugEnabled) {
/* Request and response will be printed in case restassured built-in validation fails
(e.g. .then.status(200) failing if service returned 404 instead of 200)
for other failures (e.g. field having wrong value), assertion-functions logging abilities should be used. */
config.getLogConfig().enableLoggingOfRequestAndResponseIfValidationFails(LogDetail.ALL);
}
return config;
}
LogLevel is set to DEBUG on my machine, so I get all the details of request and response.
This all worked fine until recently (though I haven't changed anything around the request). But now some requests and responses (no obvious pattern) are getting logged like this (data obfuscated):
Request method: POSTNORMAL_OUTPUT
Request URI: http://some.url/hereNORMAL_OUTPUT
Proxy: <none>NORMAL_OUTPUT
Request params: <none>NORMAL_OUTPUT
Query params: <none>NORMAL_OUTPUT
Form params: <none>NORMAL_OUTPUT
Path params: <none>NORMAL_OUTPUT
Multiparts: <none>NORMAL_OUTPUT
Headers: SOAPAction=http://some.soap.actionNORMAL_OUTPUT
Accept=text/xmlNORMAL_OUTPUT
Content-Type=application/xml; charset=UTF-8NORMAL_OUTPUT
Cookies: <none>NORMAL_OUTPUT
Body:NORMAL_OUTPUT
<ns3:Envelope xmlns:ns6="http://..." xmlns:ns5="http://..." xmlns:ns2="http://..." xmlns:ns4="http://..." xmlns:ns3="http://...">NORMAL_OUTPUT
<ns3:Body>NORMAL_OUTPUT
<ns6:wrapperTag>NORMAL_OUTPUT
<ns5:Header/>NORMAL_OUTPUT
<ns5:SearchCriteria>NORMAL_OUTPUT
<ns5:FirstName>Jane</ns5:FirstName>NORMAL_OUTPUT
<ns5:LastName>Doe</ns5:LastName>NORMAL_OUTPUT
</ns5:SearchCriteria>NORMAL_OUTPUT
</ns6:wrapperTag>NORMAL_OUTPUT
</ns3:Body>NORMAL_OUTPUT
</ns3:Envelope>NORMAL_OUTPUT
HTTP/1.1 200 OKNORMAL_OUTPUT
Date: Thu, 01 Dec 2016 13:43:03 GMTNORMAL_OUTPUT
Server: Oracle-HTTP-Server-12cNORMAL_OUTPUT
Content-Length: 4519NORMAL_OUTPUT
X-ORACLE-DMS-ECID: ssome^hashhhh_12^s000000meth11111ngDNORMAL_OUTPUT
X-Powered-By: Servlet/3.0 JSP/2.2NORMAL_OUTPUT
Keep-Alive: timeout=5, max=100NORMAL_OUTPUT
Connection: Keep-AliveNORMAL_OUTPUT
Content-Type: text/xml; charset=utf-8NORMAL_OUTPUT
Content-Language: enNORMAL_OUTPUT
NORMAL_OUTPUT
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">NORMAL_OUTPUT
<Body xmlns:ns2="http://epicor.com/" xmlns="http://schemas.xmlsoap.org/soap/envelope/">NORMAL_OUTPUT
<wrapperTag xmlns="http://epicor.com/">NORMAL_OUTPUT
<PERSON>NORMAL_OUTPUT
<ID>123</ID>NORMAL_OUTPUT
<LST_NM>DOE</LST_NM>NORMAL_OUTPUT
<FRST_NM>JANE</FRST_NM>NORMAL_OUTPUT
<ADDR_LN_1>123 BLABLA STREET</ADDR_LN_1>NORMAL_OUTPUT
<ADDR_CITY>BLABLA</ADDR_CITY>NORMAL_OUTPUT
<ADDR_ST>CA</ADDR_ST>NORMAL_OUTPUT
<ADDR_CTRY>US</ADDR_CTRY>NORMAL_OUTPUT
<ADDR_ZIP>12345</ADDR_ZIP>NORMAL_OUTPUT
<EMAIL>JANE.DOE#SOME.MAIL</EMAIL>NORMAL_OUTPUT
</PERSON>NORMAL_OUTPUT
<!-- more PERSON here, all with these "NORMAL_OUTPUT"-->
<!-- And at the end of response, all of a sudden-->
<PERSON>NORMAL_OUTPUT
<ID>345</ID>NORMAL_OUTPUT
<LST_NM>DOE</LST_NM>
<FRST_NM>JANE</FRST_NM>
<ADDR_LN_1>345 BLABLA ST APT 789</CUST_ADDR_LN_1>
<ADDR_CITY>BLABLA</CUST_ADDR_CITY>
<ADDR_ST>CA</CUST_ADDR_ST>
<ADDR_CTRY>US</CUST_ADDR_CTRY>
<ADDR_ZIP>12345</CUST_ADDR_ZIP>
<EMAIL>JANE.DOE#OTHER.MAIL</CUST_ADDR_EMAIL>
</PERSON>
<!-- one more PERSON, totally normal-->
</wrapperTag>
</Body>
</soapenv:Envelope>
Does anybody know where could this "NORMAL_OUTPUT" come from?
I've tried to debug to find out where does it come from, but while I was debugging, all was OK, though when the test finished, I found it all over the log again. I use IntelliJ, TestNG, Restassure 3.0.0 (also tried to switch to 3.0.1 and all wass the same). I run my tests via IntelliJ TestNG run. And except from this weird output everything works as expected.
UPD: I found out that it is IntelliJ Idea adding "NORMAL_OUTPUT" to the end of each line. But I couldn't yet find anything else about conditions making it do that. Any ideas?
I'm probably barking up the wrong tree with this, but I'm having some difficulty with Spring Integration and a http outbound-gateway.
I can configure it so that it makes a http POST and I get the response body as a simple String like this:
Spring Config
<int-http:outbound-gateway request-channel="hotelsServiceGateway.requestChannel"
reply-channel="hotelsServiceGateway.responseChannel"
url="http://api.ean.com/ean-services/rs/hotel/v3/list"
expected-response-type="java.lang.String"
http-method="POST"/>
Interface
public interface ExpediaHotelsService {
String getHotelsList(final Map<String, String> parameters);
}
And I can configure it so that I get a ResponseEntity back like this:
Spring Config
<int-http:outbound-gateway request-channel="hotelsServiceGateway.requestChannel"
reply-channel="hotelsServiceGateway.responseChannel"
url="http://api.ean.com/ean-services/rs/hotel/v3/list"
http-method="POST"/>
Interface
public interface ExpediaHotelsService {
ResponseEntity<String> getHotelsList(final Map<String, String> parameters);
}
Both versions of the code work. However, when returning a String I get the response body, but I don't get the http status and headers etc.
But when I use the ResponseEntity version I get the http status and headers, but I always get a null body via ResponseEntity#getBody
Is there anyway I can get both the body and the http status and headers?
(Ignoring the fact that the expedia hotels api returns JSON - at the moment I just want to get the raw body text)
Some further info which helps clarify the problem I am seeing. If I put a wire-tap on the response channel:
When I've configured it to return a simple String I get:
INFO: GenericMessage [payload={"HotelListResponse":{"EanWsError":{"itineraryId":-1,"handling":"RECOVERABLE","category":"AUTHENTICATION","exceptionConditionId":-1,"presentationMessage":"TravelNow.com cannot service this request.","verboseMessage":"Authentication failure. (cid=0; ipAddress=194.73.101.79)"},"customerSessionId":"2c9d7b43-3447-4b5e-ad87-54ce7a810041"}}, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#4d0f2471, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#4d0f2471, Server=EAN, Connection=keep-alive, id=5e3cb978-9730-856e-1583-4a0847b8dc73, Content-Length=337, contentType=application/json, http_statusCode=200, Date=1433403827000, Content-Type=application/x-www-form-urlencoded, timestamp=1433403827133}]
You can see the full response body in the payload, and notice the Content-Length being set to 337
Conversely, when I use a ResponseEntity<String> I get:
INFO: GenericMessage [payload=<200 OK,{Transaction-Id=[5f3894df-0a8e-11e5-a43a-ee6fbd565000], Content-Type=[application/json], Server=[EAN], Date=[Thu, 04 Jun 2015 07:50:30 GMT], Content-Length=[337], Connection=[keep-alive]}>, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#4d0f2471, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#4d0f2471, Server=EAN, Connection=keep-alive, id=9a598432-99c9-6a15-3451-bf9b1515491b, Content-Length=337, contentType=application/json, http_statusCode=200, Date=1433404230000, Content-Type=application/x-www-form-urlencoded, timestamp=1433404230465}]
The Content-Length is still set to 337, but there is no response body in the payload
Notice that you don't use any expected-response-type for the second case.
The RestTemplate works this way in case of no expected-response-type:
public ResponseEntityResponseExtractor(Type responseType) {
if (responseType != null && !Void.class.equals(responseType)) {
this.delegate = new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
}
else {
this.delegate = null;
}
}
#Override
public ResponseEntity<T> extractData(ClientHttpResponse response) throws IOException {
if (this.delegate != null) {
T body = this.delegate.extractData(response);
return new ResponseEntity<T>(body, response.getHeaders(), response.getStatusCode());
}
else {
return new ResponseEntity<T>(response.getHeaders(), response.getStatusCode());
}
}
As you see it really returns the ResponseEntity without body.
And Spring Integration can do nothing here on the matter...
From other side let's take a look if you really need a whole ResponseEntity as a reply back from the <int-http:outbound-gateway>.
Maybe headerMapper would be enough for you?.. For example http status is here already, even in your logs from the question:
Server=EAN, Connection=keep-alive, id=5e3cb978-9730-856e-1583-4a0847b8dc73, Content-Length=337, contentType=application/json, http_statusCode=200, Date=1433403827000, Content-Type=application/x-www-form-urlencoded,
I am actually new to REST WS but really I don't get this 415 Unsupported Media Type.
I am testing my REST with Poster on Firefox and the GET works fine for me, also the POST (when it's a application/xml) but when I try application/json it doesn't not reach the WS at all, the server rejects it.
This is my URL: http:// localhost:8081/RestDemo/services/customers/add
This is JSON I'm sending: {"name": "test1", "address" :"test2"}
This is XML I'm sending:
<customer>
<name>test1</name>
<address>test2</address>
</customer>
and this is my Resource class:
#Produces("application/xml")
#Path("customers")
#Singleton
#XmlRootElement(name = "customers")
public class CustomerResource {
private TreeMap<Integer, Customer> customerMap = new TreeMap<Integer, Customer>();
public CustomerResource() {
// hardcode a single customer into the database for demonstration
// purposes
Customer customer = new Customer();
customer.setName("Harold Abernathy");
customer.setAddress("Sheffield, UK");
addCustomer(customer);
}
#GET
#XmlElement(name = "customer")
public List<Customer> getCustomers() {
List<Customer> customers = new ArrayList<Customer>();
customers.addAll(customerMap.values());
return customers;
}
#GET
#Path("/{id}")
#Produces("application/json")
public String getCustomer(#PathParam("id") int cId) {
Customer customer = customerMap.get(cId);
return "{\"name\": \" " + customer.getName() + " \", \"address\": \"" + customer.getAddress() + "\"}";
}
#POST
#Path("/add")
#Produces(MediaType.APPLICATION_JSON)
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public String addCustomer(Customer customer) {
//insert
int id = customerMap.size();
customer.setId(id);
customerMap.put(id, customer);
//get inserted
Customer result = customerMap.get(id);
return "{\"id\": \" " + result.getId() + " \", \"name\": \" " + result.getName() + " \", \"address\": \"" + result.getAddress() + "\"}";
}
}
EDIT 1:
This is my Customer class:
#XmlRootElement
public class Customer implements Serializable {
private int id;
private String name;
private String address;
public Customer() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
Add Content-Type: application/json and Accept: application/json
in REST Client header section
The issue is in the deserialization of the bean Customer. Your programs knows how to do it in XML, with JAXB as Daniel is writing, but most likely doesn't know how to do it in JSON.
Here you have an example with Resteasy/Jackson
http://www.mkyong.com/webservices/jax-rs/integrate-jackson-with-resteasy/
The same with Jersey:
http://www.mkyong.com/webservices/jax-rs/json-example-with-jersey-jackson/
Just in case this is helpful to others, here's my anecdote:
I found this thread as a result of a problem I encountered while I was using Postman to send test data to my RESTEasy server, where- after a significant code change- I was getting nothing but 415 Unsupported Media Type errors.
Long story short, I tore everything out, eventually I tried to run the trivial file upload example I knew worked; it didn't. That's when I realized that the problem was with my Postman request. I normally don't send any special headers, but in a previous test I had added a "Content-Type": "application/json" header. OF COURSE, I was trying to upload "multipart/form-data." Removing it solved my issue.
Moral: Check your headers before you blow up your world. ;)
I had this issue and found that the problem was that I had not registered the JacksonFeature class:
// Create JAX-RS application.
final Application application = new ResourceConfig()
...
.register(JacksonFeature.class);
Without doing this your application does not know how to convert the JSON to a java object.
https://jersey.java.net/documentation/latest/media.html#json.jackson
I had the same problem:
curl -v -H "Content-Type: application/json" -X PUT -d '{"name":"json","surname":"gson","married":true,"age":32,"salary":123,"hasCar":true,"childs":["serkan","volkan","aybars"]}' XXXXXX/ponyo/UserModel/json
* About to connect() to localhost port 8081 (#0)
* Trying ::1...
* Connection refused
* Trying 127.0.0.1...
* connected
* Connected to localhost (127.0.0.1) port 8081 (#0)
> PUT /ponyo/UserModel/json HTTP/1.1
> User-Agent: curl/7.28.1
> Host: localhost:8081
> Accept: */*
> Content-Type: application/json
> Content-Length: 121
>
* upload completely sent off: 121 out of 121 bytes
< HTTP/1.1 415 Unsupported Media Type
< Content-Type: text/html; charset=iso-8859-1
< Date: Wed, 09 Apr 2014 13:55:43 GMT
< Content-Length: 0
<
* Connection #0 to host localhost left intact
* Closing connection #0
I resolved it by adding the dependency to pom.xml as follows. Please try it.
<dependency>
<groupId>com.owlike</groupId>
<artifactId>genson</artifactId>
<version>0.98</version>
</dependency>
I get the same issue.
In fact, the request didn't reach the jersey annoted method.
I solved the problem with add the annotation: #Consumes(MediaType.APPLICATION_FORM_URLENCODED)
The annotation #Consumes("/") don't work!
#Path("/"+PropertiesHabilitation.KEY_EstNouvelleGH)
#POST
//#Consumes("*/*")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void get__EstNouvelleGH( #Context HttpServletResponse response) {
...
}
In my case, the following dependency was missing:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
Don't return Strings in your methods but Customer objects it self and let JAXB take care of the de/serialization.
I had the same issue, as it was giving error-415-unsupported-media-type while hitting post call using json while i already had the
#Consumes(MediaType.APPLICATION_JSON)
and request headers as in my restful web service using Jersey 2.0+
Accept:application/json
Content-Type:application/json
I resolved this issue by adding following dependencies to my project
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.25</version>
</dependency>
this will add following jars to your library :
jersey-media-json-jackson-2.25.jar
jersey-entity-filtering-2.25.jar
jackson-module-jaxb-annotations-2.8.4.jar
jackson-jaxrs-json-provider-2.8.4.jar
jackson-jaxrs-base-2.8.4.jar
I hope it will works for others too as it did for me.
just change the content-type to application/json when you use JSON with POST/PUT, etc...
In case you are trying to use ARC app from google and post a XML and you are getting this error, then try changing the body content type to application/xml. Example here
I encountered the same issue in postman, i was selecting Accept in header section and providing the value as "application/json". So i unchecked and selected Content-Type and provided the value as "application/json". That worked!
If none of the solution worked and you are working with JAXB, then you need to annotate your class with #XmlRootElement to avoid 415 unsupported media type
I had similar problem while using in postman.
for POST request under header section add these as
key:valuepair
Content-Type:application/json
Accept:application/json
i hope it will work.