Play Framework Cross Domain Web Service - json

I've been trying to create a Web Service with play framework. I've searched some tutorial and resource about this and end up using the renderJSON() method to provide JSON RESTful web service.
But, there seems to be a problem with that when I try to consume the web service with JQuery. If I use JSON, it fails with this error Origin http://localhost is not allowed by Access-Control-Allow-Origin. Which seems to be a cross-domain problem. Then I try to use JSONP as the datatype when JQuery trying to consume the web service, again there's an parseError problem.
After some research, I found that to provide a web service to the JSONP request. We need to provide something like a callback function. So it's not enough to return only the JSON object to the client. If I'm not mistaken, The format should look something like {#callback-function}{#JSON-data}
How to make a Cross-Domain web service in Play Framework? Is there any workaround for this?
Is there anyway to keep the JSON format of a/a list of object to a string instead of render the object as JSON directly as done by calling renderJSON()?

Here is what I did. In my routes I set the format of the response to javascript with:
GET /api/projects.json Api.projects(format:'json')
Then for the controller in Api I used flexjson and did this:
public static void projects(String p) {
JSONSerializer json = new JSONSerializer();
List<Project> projects = Project.all().fetch(3);
renderJSON(p + '(' + json.serialize(projects) + ')');
}
So now jsonp calls can be made by hitting /api/projects.json?p=whatever and the return will be:
whatever([{"your":"json"}])
You can also use flexjson to only serialize the parts of the object that you want to expose.
Testing it with jquery:
$.ajax({
url: "http://api/projects.json",
dataType: "jsonp",
jsonp : 'p',
crossDomain : true,
success: function(data){
console.log(data);
}
});

This is working great but you can use Gson object directly instead of the flexjson object.

Related

Yii2 HttpClient: Access Request instance from Response instance

Using yii2-httpclient, what is the correct way to access the corresponding yii\httpclient\Request instance from the resulting yii\httpclient\Response object?
I am trying to write a custom XML parser which needs to know what URL it is parsing. It does not seem to be possible to access the original Request (through which I could get the URL) from a parser instance (only the Response).
I have considered utilizing yii\httpclient\Client::EVENT_AFTER_SEND to copy the request into a variable, but that would not be thread-safe, so I need a better solution.
If your parser needs to know URL of request to parse response, it is probably not a parser and you're overusing parser API and ParserInterface. I suggest to create some component which will wrap and hide all request-response-parser logic. Then you will be able to implement custom parser and call it manually:
public function get($url) {
$client = new Client();
$response = $client->createRequest()
->setUrl($url)
->send();
return (new MyParser($url, $response))->getContent();
}

Other technologies that can perform DTO request response like WCF

I have a WCF service that allows me make a request using an DTO and replies with a DTO for a WPF application. For example I pass a filter object for products which has a few properties for things I want to filter on and a couple of extras for paging, (the server will take care processing the filter object and getting the data) an example is like this.
public async Task<ObservableCollection<ProductListItem>> GetProductList(ProductFilter filter, int startIndex, int pageSize, string sortBy)
I am wondering if there exists any other technologies beside WCF that allow such an operation, From my preliminary research which may be quite off is that WebAPI uses the GET, POST, PUT verbs and routing rules which is quite different.
ServiceStack looks like it might be able to do this I can see on slide 37 at https://servicestack.net/features
it says.
List<Product> productOver2Bucks = client.Get(new FindProducts{PriceGreaterThan = 2})
Which seems pretty close but might still require Rest verbs as it uses a Get().
I don't know it it is FUD or not but I have been reading that soap over WCF is believed by some to be a legacy technology and JSON is the way of the future. So is there a replacement technology that will work with a method signature to the one I have above? That i could call from platforms such as Windows universal applications.
In ServiceStack if you design your Service with the Any method name, e.g
public object Any(Request request)
{
return new Response { ... };
}
This will allow calling this Service from Any HTTP Verb on any Format or endpoint (e.g. JSON, XML, MsgPack, Protocol Buffers, SOAP, Message Queue's, etc).
Also you don't need to define any [Route] for your Request DTO's since it will automatically fallback into using the pre-defined Routes when none are available.
public class Request : IReturn<Response> { ... }
public class Response { }
So with the above Service you can use ServiceStack .NET ServiceClients to call the API's using any verb, e.g:
var client = new JsonServiceClient(baseUrl);
Response response = client.Get(new Request { ... });
Response response = client.Post(new Request { ... });
When preferred you can also use the async API's, e.g:
var response = await client.GetAsync(new Request { ... });
var response = await client.PostAsync(new Request { ... });
Which if you don't care for using verbs you can use the generic Send API, e.g:
Response response = client.Send(new Request { ... });
Which just uses POST underneath, although it's highly recommended to use Get for "read only" queries as it will allow the Services HTTP responses to be cached by any intermediate HTTP Middleware or proxies.
Add ServiceStack Reference
Also if you're coming from WCF you'll also enjoy ServiceStack's, Add ServiceStack Reference which provides a number of advantages over WCF's Add Service Reference feature but still provides the same utility in being able to generate a typed API from a remote url for:
C# Add Reference
F# Add Reference
VB.NET Add Reference
TypeScript Add Reference
With more languages to follow.
Advantages over SOAP
Whilst ServiceStack still enables WSDL's, XSD's for your Services so they can be called from SOAP 1.1/1.2 endpoints for legacy compatible reasons - there are a number of reasons why using clean HTTP and JSON/XML API's are preferred.

WebAPI Model Binding from JSON

I am creating an application using Durandal, with WebAPI as the server. I have a KendoUI grid that displays the data from the server correctly and functions properly until the POST or PUT methods are called. Here is my GET method:
and you can see that that data binds to the UI (used the data-bind extensibility in Durandal to change to kendo bindings):
Then I edit the data in the Grid and it passes the changes inside the request to the server as you can see in this Fiddler result:
On the server side I cannot get the data that is passed from the client to bind to anything I place as a parameter for the method on the POST or PUT.
I realize this is a couple different technologies to troubleshoot (e.g. Durandal, KnockoutJs, Kendo DataBinding, and WebAPI) but I think the fundamentals are working, the data is retrieved and bound to the UI and it is posted back when changed, but the WebAPI endpoint cannot bind to the data.
How can I get the passed "models" array to bind through the ModelBinding structure in WebAPI?
UPDATE- Here is the helpful JSFiddle that gave me the correct Content-Type to add: http://jsfiddle.net/Xhrrj/1/
new kendo.data.DataSource({
transport: {
read: {
type: "POST",
url: "../cccs/service.svc/SupplierSearch",
contentType: "application/json; charset=utf-8",
dataType: 'json'...
this is coming from the Telerik forum here
It looks as if it was mixing up form-urlencoded with json format - if you lookat the decoded string it is sending models= and then urlencoded JSON objects follow.
From my experience I think that your PUT end point declaration needs to look like this:
[HttpPut]
public void Put([FromBody]IEnumerable<Product> models) { }
So you need the FromBody attribute because your product array is in the body (I think?) and not in the url of the request.

Calling .Net web service from jQuery+Ajax

I'm trying to call a homemade vb.net web service using jQuery+Ajax and I'm struggling with the specifics.
Here's a small function exposed as a web method:
<WebMethod()> <ScriptMethod(ResponseFormat:=ResponseFormat.Xml, UseHttpGet:=True)> _
Public Function GetAllVotes() As XmlDocument
Dim theVotes = getVotes()
Dim strResult As String = theVotes.XMLSerialize
Dim doc As XmlDocument = New XmlDocument()
doc.LoadXml(strResult)
Return doc
End Function
After looking the web I've added the ScriptMethod attributes since i was returning XML but feel free to tell me i don't need them if that's the case.
Then, on the client side, this is the code :
function getVotes() {
$.support.cors = true;
$.ajax({
type: "GET",
contentType: "application/json",
url: "http://nhrd635:8008/votingmanager.asmx/GetAllVotes",
data: {},
dataType: "xml text jsonp",
success: function(msg) {
// Hide the fake progress indicator graphic.
// Insert the returned HTML into the <div>.
$('#myPlaceHolder').html(msg);
},
error: function(msg) {
$('#myPlaceHolder').html(msg);
// alert(msg);
}
});
}
I've tried many .. many variations of this code, using post or get, changing the content-type, with or without charset=utf-8. with and without double quotes on data: {}.
i use firebug to trace the output my request. only when i set dataType to jsonp do i ever get a result, but in all instances, the code ends up on the "error" function, even when status give 200 OK. but i know that setting it to jsonp is wrong since that gets my xml treated as actual javascript...
I've read very useful blog entries from a guy on encosia.
(sample: http://encosia.com/3-mistakes-to-avoid-when-using-jquery-with-aspnet-ajax/)
but even following his examples i am unable to get a proper return.
am i doing something wrong that's very obvious? is it the fact that i am returning an xml string rather than a json serialized string?
With more perusing of Stack Overflow and the help of Dave Ward from Encosia, I've managed to solve my problem. I've thought I should post my final solution here, in case that helps someone in the future.
First of all, Web Services were a bad way of doing it, I went with the HttpHandler solution, as suggested by Dave Ward in reply to my original question.
Returning XML was also a poor choice, that I wasn't really aware of. I added a reference to JSon.net to my project and used it to transform my object into a Json string.
I really wanted to stick to ".net only" to transform into a json string, as suggested in Dave's blog post, but somehow I struggled to learn how to instruct .net to automatically transform into Json as in Dave's example, so i took an easy way out with Json.net to "get it working"
Then, in my HttpHandler, I had the response string follow the instructions on this post from StackOverflow:
https://stackoverflow.com/a/3703221/1060133
in my case, it was :
context.Response.Write(String.Format("{0}({1});", context.Request("callback"), jsonVotes))
The jquery call also used the instructions in the above post.
Interesting note, even in a parameter-less call, you have to send empty data like so:
$.getJSON('http://url/httpHandler.ashx?callback=?', {},
function(data) {
alert(data);
}
);
Best of luck...
I think most of your trouble here probably stems from the cross-origin request (even making a request across different ports on the same machine counts). That's why you were able to get a glimmer of it working when you switched to JSONP. Unfortunately, ASMX "ScriptServices" don't support JSONP, so the data your WebMethod returned wouldn't be a valid parameter to the JSONP callback function that jQuery injects.
The best solution, if at all possible, is to get the service running on the same domain as the page that's calling it. There are various solutions to the cross-origin problem, but none of them are as widely compatible/reliable as a simple XHR request to the same domain that the page making the request resides on.
If you can't do that, consider enabling CORS support for the site serving up votingmanager.asmx. That doesn't work in most versions of IE, but will allow cross-origin requests in other browsers. More info on how to do that here: http://encosia.com/using-cors-to-access-asp-net-services-across-domains/
Tangentially, I'd avoid the extra XML serialization layer if possible. If getVotes() returns something like a List, use that as your return type and let ASP.NET automatically serialize the collection as JSON and then jQuery will automatically convert that to a JavaScript array in your success handler. More info about that here: http://encosia.com/asp-net-web-services-mistake-manual-json-serialization/

GWT RequestBuilder POST JSON application/json

I'm trying to send JSON data to a server.
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url);
builder.setHeader("Content-Type", "application/json");
builder.setRequestData(getJSONString());
builder.setCallback(myCallbackObject);
builder.send();
I do this in eclipse and I saw in the TCP/IP Monitor that my JSON Data is not transmitted as post in the request. If I ask for
builder.getRequestData();
I can see the JSON data is right there.
What do I need for the data to get on the server?
You might be running into the browser's same-origin policy if the url that you are attempting to connect to is not from the same origin as your GWT nocache.js file. Is your callback's onFailure() being called? Also, see if Request.getSatusCode() returns 0, which is indicative of SOP problems.
To simply post JSON data with HTTP post, you can use GWT Overlay Types to define a post method using JQuery. A possible snapshot:
public static native void post(String json) /*-{
var data = JSON.parse(json);
$wnd.$.post(url, data);
}-*/;
I don't know if it is necessary to add JQuery to your HTML. I did, and worked. You can do this by adding the following line to your .html file:
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Then, you can simply call that method in your java code:
post(json);
(: