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?
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();
}
}
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
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'm developing an application, where there are a number of subdomains, for example api.*.com, which is responsible for REST request processing. I'm trying to set cookie for subdomains, using:
public class PlayerTokenUtils {
final private static int DEFAULT_AGE = (int) TimeUnit.DAYS.toSeconds(7);
private PlayerTokenUtils() {
throw new IllegalAccessError();
}
public static PlayerToken updateResponse(String token, HttpServletResponse response) {
Cookie cookie = new Cookie("player", token);
cookie.setPath("/");
cookie.setHttpOnly(true);
cookie.setDomain(".mavarazy.com");
cookie.setMaxAge(DEFAULT_AGE);
response.addCookie(cookie);
return token;
}
}
I see in Firebug, that everything works fine for registration Request (http://api.mavarazy.com:3333/registration/base/signin) & Response contains:
Set-Cookie player=rHIHtISWzw; Domain=.mavarazy.com; Expires=Sun, 02-Nov-2014 07:44:54 GMT; Path=/; HttpOnly
But further requests to the server with api.mavarazy.com do not contain player Cookie, returned from Set.
The environment for testing, that I'm using:
I modified my /etc/hosts
127.0.0.1 mavarazy.com
127.0.0.1 api.mavarazy.com
My REST server is running Tomcat.
All REST requests for the browser go through Node.js proxy.
What am I doing wrong? What is the right format for player Cookie in this case?
I've found an issue with jQuery configuration in my Backbone application.
Adding
$.ajaxSetup {
...
xhrFields: {
withCredentials: true
}
...
}
Solved the problem.
I'm attempting to post a JSON document from an AngularJS app to a Jersey REST service. The request fails, informing me that:
XMLHttpRequest cannot load http://localhost:8080/my.rest.service/api/order/addOrder. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
Jersey REST Post Function
I have enabled (what I believe to be) the appropriate headers: Access-Control-Allow-Origin and Access-Control-Allow-Methods on the response, as seen in the method below:
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#Path("/addOrder")
public Response addOrder(DBObject dbobject) {
DB db = mongo.getDB("staffing");
DBCollection col = db.getCollection("orders");
col.insert(dbobject);
ObjectId id = (ObjectId)dbobject.get("_id");
return Response.ok()
.entity(id)
.header("Access-Control-Allow-Origin","*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
.allow("OPTIONS")
.build();
}
Angular JS Controller
I've declared the app and configured the $httpProvider with all of the settings suggested in similar Stack Overflow questions:
var staffingApp = angular.module('myApp', ['ngRoute', 'ui.bootstrap']);
myApp.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.headers.common["Accept"] = "application/json";
$httpProvider.defaults.headers.common["Content-Type"] = "application/json";
}]);
I've also created this controller to open a modal and handle the form:
var modalCtrl = function($scope, $modal, $log, $http, $location) {
$scope.order = {
activityTitle : null,
anticipatedAwardDate : null,
component : null,
activityGroup : null,
activityCategory : null,
activityDescription : null
};
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'addOrder.html',
windowClass: 'modal',
controller: modalInstanceCtrl,
resolve: {
order : function () {
return $scope.order;
}
}
});
modalInstance.result.then(function (oid) {
$log.info("Form Submitted, headed to page...");
$location.path("/orders/" + oid);
}, function() {
$log.info("Form Cancelled")
});
};
};
var modalInstanceCtrl = function ($scope, $modalInstance, $log, $http, order) {
$scope.order = order,
$scope.ok = function () {
$log.log('Submitting user info');
$log.log(order);
$log.log('And now in JSON....');
$log.log(JSON.stringify(order));
$http.post('http://localhost:8080/my.rest.service/api/order/addOrder', JSON.stringify(order)).success(function(data){
$log.log("here's the data:\n");
$log.log(data);
$modalInstance.close(data._id.$oid)
});
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
myApp.controller('modalCtrl', modalCtrl);
To no avail, I've tried:
removing .allow("OPTIONS") from the response headers.
removing the $httpProvider configuration from the application
changed the $httpProvider configuration to call myApp.config(function ($httpProvider) {...}), passing the function itself rather than the array.
Get requests work with the same configuration:
#GET
#Path("/listall/")
#Produces(MediaType.APPLICATION_JSON)
public Response listAll(){
DB db = mongo.getDB("staffing");
DBCollection col = db.getCollection("orders");
List<DBObject> res = col.find().limit(200).toArray();
return Response.ok()
.entity(res.toString())
.header("Access-Control-Allow-Origin","*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
.allow("OPTIONS")
.build();
}
with this controller that works fine:
myApp.controller('orderListCtrl', function ($scope, $http){
$http.get('http://localhost:8080/my.rest.service/api/order/listall').success(function(data) {
for (var i = 0; i < data.length; i++) {
if (data[i].description.length > 200) {
data[i].shortDesc = data[i].description.substring(0,196) + "...";
} else {
data[i].shortDesc = data[i].description;
}
};
$scope.orders = data;
});
});
Update #1:
I've tried the same request on a same origin basis, essentially serving the Angular application alongside the REST service from locahost:8080. This configuration worked, but required a slight change and some general clean up in my code, which I've edited above.
The Post still fails as a CORS request, however so I'm still looking for the missing piece in this configuration.
Update #2:
I've investigated the headers of the working request as they're delivered to the browser and compared them with the non-working request.
The working get request returns the following headers with its response:
The non-working post request returns headers with its response, but is missing the Access-Control-Allow-Origin header:
I believe this has now become an issue of the headers being stripped off of the response prior to returning it to the client, which would then cause the browser to fail the request.
Update #3:
Submitting a test POST request to the same URL from Chrome's REST Console extension returns the appropriate response headers, as seen in the screencap below.
At this point, I can't determine what's removing the headers between Jersey and my Angular client, but I'm fairly confident that's the culprit.
The problem turned out to be inadequate handling of the OPTIONS request sent in pre-flight prior to the POST request with the proper cross origin headers.
I was able to resolve the issue by downloading and implementing the CORS filter found at this page: http://software.dzhuvinov.com/cors-filter-installation.html.
If you're experiencing a similar problem, follow the instructions and test to see that your OPTIONS request is no longer failing, and is immediately followed by your successful request.
Best way is to add Jersey Response filter which will add the CORS headers for all the methods. You don't have to change your webservices implementation.
I will explain for Jersey 2.x
1) First add a ResponseFilter as shown below
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
public class CorsResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
responseContext.getHeaders().add("Access-Control-Allow-Origin","*");
responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
}
}
2) then in the web.xml , in the jersey servlet declaration add the below
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>YOUR PACKAGE.CorsResponseFilter</param-value>
</init-param>
I had faced similar CORS error while calling my Restful service (implemented in java - Jersey) from angularjs. To fix it I added Access-Control-Allow-Origin: * in response header. I added below :
response.addHeader("Access-Control-Allow-Origin", "*");
For more information you can check - http://enable-cors.org/server.html
CORS error occurs typically when your angularjs code (web project) and webserivce code (server side project) are on different IP and port no.
Your webservice implementation looks correct. So just to check, try running them on localhost on same port (eg. 8080). It should work there if all code is correct.
In order to run them separately try adding Access-Control-Allow-Origin: * in webservice implementation as shown above.
Hope this helps.
Actually, you have other solution that does not need a filter. Adding the Access-Control-Allow-* headers to the GET request, is not enough, you have to create an OPTIONS endpoint to allow browsers do the pre-flight request, i.e.:
#OPTIONS
public Response corsMyResource(#HeaderParam("Access-Control-Request-Headers") String requestH) {
ResponseBuilder rb = Response.ok();
return buildResponse(rb, requestH);
}
see https://kdecherf.com/blog/2011/06/19/java-jersey-a-cors-compliant-rest-api/ for reference.