REST not working on bluemix due to CORS - json

I am new to bluemix, I have simple Rest service with few functions and called from angular js.
Here is below in Rest service class:
#POST
#Path("getTest")
#Produces("application/json")
public Response getTest(#QueryParam("accountId") String accountId) throws Exception, IOException {
System.out.println("HelloResource.getTest() "+accountId);
...
...
return Response.ok(dbData).header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods","POST, HEAD, GET, DELETE, PUT, OPTIONS")
.build();
}
Further, in client angular js is invoking this rest service in
$scope.searchcall = function(acctid)
{
$http({
method: 'POST',
url: 'http://javarestapi61.mybluemix.net/api/hello/getTest',
headers: {'Content-Type': 'application/json','Access-Control-Allow-Origin': '*'},
data: {"accountId": $scope.accountId}
}).success(function (data)
{
//never been successful onblue mix
}).error(function(data) {
alert("failure1"); //always landing here..
});
};
Looks like this is best solution which i tried. I am not sure, what i can try further to allow explicit permission on bluemix Rest application. BTW above app when deployed on local liberty, it works well.
Also i can access the same Rest service on bluemix, if i change return type as String and access via browser..(without even any additional permission)
Further, I saw that Chrome network details shows that, Preflight request go and returns with 200 (OPTIONS) then second request is actual POST which never succeeds.

What you can do is modify the method to :
public Response getTest(#QueryParam("accountId") String accountId, HttpServletRequest req) throws Exception, IOException{
//then your code
}
Now after that add the following:
since the browser looks for http://127.0.0.1:8080 in the Allow Origin Header, you need to make sure this is getting added in the following line:
response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8080");
To get the http://127.0.0.1:8080 you can use :
request.getRemoteAddr() + ":" + request.getRemotePort();
But if the browser looks for localhost then go for the :
request.getRemoteHost().
Avoid adding * because some browsers will not still allow that.
Hope that solves your problem.
And of course add this to the header:
response.setHeader("Access-Control-Allow-Methods","POST, HEAD, GET, DELETE, PUT, OPTIONS")

Finally when everything else that i tried did not work, below solution worked for me..
I have just added another separate url in my rest service class..
#Path("/getTest")
#OPTIONS
public Response getOptions() {
return Response.ok().header("Access-Control-Allow-Origin", "*").header("Access-Control-Allow-Methods", "POST, GET, PUT, UPDATE, OPTIONS").header("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With").build();
}
Basically response of options have the "allowed" methods given by service..

Related

WebAPI: A callback parameter was not provided in the request URI

I am executing a post method in my API using fiddler I get error "A callback parameter was not provided in the request URI.". However, this works for get method.
I have seen several answers to this question, and as per the error I need to specify a callback parameter. However, I'm not sure how to do this using fiddler.
In response to one of those answers from Can I make a jQuery JSONP request without adding the '?callback=' parameter in URL? . I've tried the following in fiddler and I get the same error..
url: http://velopoint-api.localhost.dev/api/v1/tasks?callback=foo
header:
User-Agent: Fiddler
Host: velopoint-api.localhost.dev
ContentType: application/json; charset=utf-8
Authorization: basic "UNQUOTED"
Content-Length: 47
jsonp: true
jsonpCallback: jsonCallback
dataType: jsonp
request body
{ "Title":"New Task", "DueDate":"20-jul-2014" }
Startup
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
// Change Formater to use CamelCasePropertyNamesContractResolver
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().FirstOrDefault();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
/* Support JsonP */
//register JSONP media type formatter
config.Formatters.Insert(0, new JsonpMediaTypeFormatter(jsonFormatter));
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
...
}
After playing around a little, I've finally figured it wasn't actually routing to the post method.
My header is now
User-Agent: Fiddler
Host: velopoint-api.localhost.dev
Authorization: basic UNQUOTED
Content-Length: 224
Content-Type: application/json
I fixed the problem by specifying Route attribute to my Post method and passing an empty string to the pattern parameter, both on the Get and the Post (as I already have the RoutePrefix attribute specified on the class.
[RoutePrefix("api/v1/tasks")]
[VeloPointAuthorise(perUser: true)]
public class TaskController : BaseApiController
{
[HttpGet]
[Route(template:"", Name = "TaskRoute")]
public HttpResponseMessage Get(int page = 0)
{
....
}
[HttpPost]
[Route(template:"")]
public HttpResponseMessage Post([FromBody] OrganiserTaskModel model)
{
....
}

Angular $http.get to localhost, always returns 404. 200 in browser

I can't create a successful get request in Angular 1.2.13.
var getProgress = function() {
var promise = $http({method: 'GET', url: 'http://localhost:8080/project/local/some/getStatus'});
promise.then(function(success) {
alert(JSON.stringify(success));
}, function(error) {
alert(JSON.stringify(error));
}, function(notify) {
alert(JSON.stringify(notify));
});
};
So I'm trying to receive some JSON from my REST web service. To test the service, I access it from the browsers(IE9,Firefox 27, Chrome 33) works fine in all of them.
The above code using angular however, always prompts me with the error:
*{"data":"","status":404,"config":{"transformRequest":[null],"transformResponse":[null],"method":"GET","url":"http://localhost:8080/project/local/some/getStatus","headers":{"Accept":"application/json, text/plain, /"}}}*
Using wireshark I check the HTTP request and HTTP response, both calling the web service from browser and from angular returns 200, and the desired json object!! Nevertheless angular prompts me with 404.
When I make the get request from Firefox USING ANGULAR and debug using Findbugs, in the console I get HTTP Code 200, nevertheless Angular says 404. Tried debuging angular's code to no avail, can't see anything strange.
The above code works correctly in IE9!
Any suggestions are appreciated.
#GeoffGenz && #BKM were right. CORS is to blame. I imagine there are several ways around the problem.
During development, don't debug your page by loading it from file, instead deploy it, inside of your web server, and debug from there using firebugs. This was my case. I was loading the webpage from file:///...... and I was making a request to http://localhost... Because the protocol is different (file vs http) the request has different origin, as explained in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript . Once deployed in JBoss everything works.
A second solution, which I haven't try would be to use
JSONP Angular
The last one would be to create a ContainerResponseFilter to alter the headers?
#Provider
public class RespFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext reqContext, ContainerResponseContext respContext) throws IOException {
respContext.getHeaders().putSingle("Access-Control-Allow-Origin", "*");
}
}
I had the same problem. but the cause was simply that I added EnableCors attribute to controller but I forgot to enable CORS in WebAPI configs.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.EnableCors();
....
}
}
Try this:
$http({method: 'GET', url: 'http://localhost:8080/project/local/some/getStatus'}).
success(function(data, status, headers, config) {
alert(JSON.stringify(data));
}).
error(function(data, status, headers, config) {
alert(JSON.stringify(data));
});

Pass additional parameters through a httpservlet request

The function deleteGroup() deletes a group and one of the parameters for this method is groupDN. In the front end I use jquery to specify a HTTP delete request. I was previously sending the groupDN by appending it with the URL. I now want to try sending this parameter embedded with the HttpServletRequest. The code for both the front and back end is below. Could someone guide me to the proper way of sending/receiving additional parameters within a request?
Front end
function removeGroup(groupDN) {
var deleteGroupDetails=new Object();
deleteGroupDetails.groupDN=groupDN;
jQuery.ajax({
type : 'DELETE',
url : 'api/groupService/deleteGroup,
dataType : 'json',
data : JSON.stringify(deleteGroupDetails),
contentType : 'application/json',
async: false,
success : function(response) {
utilDisplayMessage(response,"SUCCESS");
window.location.reload();
},
error : function(obj, error, errormsg) {
utilDisplayMessage(obj.responseText,"ERROR");
}
});
}
deleteGroupDetails contains the groupDN parameter which I think I could pass along with the request.
The back end in Java
#DELETE
#Path("deleteGroup")
#Produces({MediaType.APPLICATION_JSON})
public String deleteGroup(#Context HttpServletRequest request) throws Exception {
String groupDN = request.getParameter("groupDN");
}
Don't use a request body with a HTTP DELETE request. What you try to do is not REST but RPC (Remote Procedure Call) over HTTP. Don't do this.
The HTTP DELETE verbs tells the server to delete the resource identified by the URI. So if you do
DELETE /path/to/resources/123
the resource identified by this URI shall be deleted.
If you want to delete more than one entity on the server with one DELETE request, craft your resource URI to mean a collection of entities. For example:
DELETE /path/to/resources/?groupDN=foo
could mean: Delete all entities that somehow match the filter groupDN=foo.

Returning JSON with web api controller MVC

I am trying to convert a regular old controller I was using to an API controller and am having a little bit of difficulty. What these series of functions do is, in the jQuery, it iterates over a file containing all the usernames of employees and for each username it makes a call to the PopulateEmployee method in my webapi controller which should return JSON and then populate a results div.
When manually navigating to
..domain../staffinformation/populateemployee/employeeusername
I get the error
This XML file does not appear to have any style information associated with it. The
document tree is shown below.
<Error>
<Message>
The requested resource does not support http method 'GET'.
</Message>
</Error>
Please note that the div it will be populating is a partial view in an Umbraco CMS page and I don't think that is the problem but if you guys think differently please tell me.
There has to be something I am missing either with webAPI routing or something else.
Thanks for your help.
Here's the codez.
Please notice that this method has the HttpPost tag
public class StaffInformationController : ApiController
{
[System.Web.Http.ActionName("PopulateEmployee")]
[System.Web.Http.HttpPost]
public StaffListing PopulateEmployee(string id)
{
//do error checking on input
StaffListing staffListing = new StaffListing(id);
//populate other fields
return staffListing;
}
}
The routing set up for the api controller
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
The jQuery call specifying use of 'POST', please forgive the trickiness of the recursive call in this function.
function getEmployeeObjectByIndex() {
$.ajax({
url: $('#root').val() + '/api/StaffInformation/PopulateEmployee',
type: 'POST',
async: true,
contentType: 'application/json, charset=utf-8',
data: JSON.stringify({ 'username': lines[i] }),
success: function (staffObject) {
if (!(staffObject.Name == undefined)) {
buildHtmlStrings(staffObject);
}
i++;
getEmployeeObjectByIndex(); //recursive call
}
});
}
manually navigating to that address throws the error because, when manually navigating you are doing a GET (and your method only allows POSTs).
You should fire up Fiddler and watch the ajax POST request and response to see how the server is responding / your request is being made
Jquery ------> web api
Web API has one property i.e. CONTENT NEGOTIATION means you send any data and accept any data as you want.
$.ajax({
contentType: 'application/json, charset=utf-8',
// this is sending your data of datatype json to server, here you send any type of data
accept: 'application/json',
//this is receiving/getting data form server to client...
// SO HERE YOU GET JSON DATA AS YOU WANT only mention which data of datatype u want...
//if you sending xml and you want json so only write accept as json it get automatically converted into your required datatype..by MediaTypeFormatter
});

IE9 JSON Data "do you want to open or save this file"

Started testing my jQuery applications with IE9. Looks like I may be in for some trouble here.
I noticed that when I return JSON data back to the Javascript methods I always get this Prompt that says: "Do you want to open or save this file?" and provides me with 3 buttons: Open, Save and Cancel. Of course, my javascript is taking actions based on the values set in the JSON object but since IE9 doesn't pass it over to the script, I cannot execute the follow up action from there on.
Anyone else facing this issue? Here is a snapshot.
If anyone is using ASP.net MVC and trying to fix this issue - I used the following built in methods in the MVC framework. Simply update the content Type and encoding on the JsonResult.
public ActionResult Index(int id)
{
// Fetch some data
var someData = GetSomeData();
// Return and update content type and encoding
return Json(someData, "text/html", System.Text.Encoding.UTF8,
JsonRequestBehavior.AllowGet);
}
This fixed the issue for me!
(Answer originally posted for this question.)
If using MVC, one way of handling this is to implement a base controller in which you override (hide) the Json(object) method as follows:
public class ExtendedController : Controller
{
protected new JsonResult Json(object data)
{
if (!Request.AcceptTypes.Contains("application/json"))
return base.Json(data, "text/plain");
else
return base.Json(data);
}
}
Now, your controllers can all inherit ExtendedController and simply call return Json(model); ...
without modifying the response content type for those browsers which play nicely (not <=IE9 !)
without having to remember to use Json(data, "text/plain") in your various Ajax action methods
This works with json requests which would otherwise display the "Open or Save" message in IE8 & IE9 such as those made by jQuery File Upload
I also faced this problem yesterday with WebAPI which returned a list of URLs (of asynchronously uploaded files).
Just set content type to "text/html" instead of default "application/json; charset=UTF-8" of WebAPI services. I got response as a JSON string and then used $.parseJSON to convert it to JSON object.
public async Task<HttpResponseMessage> Upload()
{
// ...
var response = Request.CreateResponse(HttpStatusCode.OK, files);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
return response;
}
// result is an iframe's body content that received response.
$.each($.parseJSON(result.html()), function (i, item)
{
console.log(item.Url);
});
In my case when contentType in response header is "application/json; charset=UTF-8", the IE 9 shows that Prompt. But changed to "text/html" then the prompt does not show, although all otter browsers are fine with the "application/json; charset=UTF-8".
Actually, you were right #EricLaw. After setting the content type in the Json result, it worked.
I had to add the following lines:
result.ContentEncoding = System.Text.Encoding.UTF8;
result.ContentType = "application/json; charset=UTF-8