Jersey Response with JSONP - jersey

I am creating a Jersey API service to be called from browsers and java clients. The code works when called from the same domain, but when called from some other domain it did not work, so I created a tried to wrap the responseJson String with JSONWithPadding. It is still sending the normal response back and not the response I am looking. The implementation of service is Jersey ( Maven Path Below:
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
<version>${jersey.version}</version>
</dependency>
Sample Code for
#Path("/v1/sample")
#Produces({"application/json"})
public class SampleService
{
#GET
#Path("/service1")
#Produces({"application/json"})
public Response notWorking(#Context UriInfo uriInfo)
{
String result = "{\"name\":\"John Johnson\",\"street\":\"Oslo West 16\",\"phone\":\"555 1234567\"}";
String callbackStr = (String)uriInfo.getQueryParameters().getFirst("callback");
System.out.println("callbackStr ="+callbackStr);
JSONWithPadding paddedJson = new JSONWithPadding(result, callbackStr);
return Response.status(200).entity(paddedJson).build();
}
}
call 1: http://localhost:8080/myapi/v1/sample/service1
Response: {"name":"John Johnson","street":"Oslo West 16","phone":"555 1234567"}
call 2: http://localhost:8080/myapi/v1/sample/service1?callback=?
Response: {"name":"John Johnson","street":"Oslo West 16","phone":"555 1234567"}
In call 2: Response I am looking for is
?({"name":"John Johnson","street":"Oslo West 16","phone":"555 1234567"})
Surely I am missing something, but could not figure out.

There was several things to work on in your example.
JSONP needs one of the following response contentTypes: "application/javascript", "application/x-javascript", "application/ecmascript", "text/javascript", "text/x-javascript", "text/ecmascript", "text/jscript". It doesn't work with "application/json".
You have to make sure that the name of the QueryParam recieved is called "callbackStr". It is usually called "callback".
You can return directly the JSONWithPadding object.
#GET
#Path("/service1")
#Produces({"application/javascript"})
public JSONWithPadding nowItWorks(#QueryParam("callbackStr") String callbackStr, #Context UriInfo uriInfo)
{
String result = "{\"name\":\"John Johnson\",\"street\":\"Oslo West 16\",\"phone\":\"555 1234567\"}";
System.out.println("callbackStr ="+callbackStr);
return new JSONWithPadding(result, callbackStr);
}
Hope this helps.

Related

Documenting possible values for Map in Swagger

I have a JAX-RS method which takes a Map to facilitate a partial update.
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("/{id}/edit")
public Response edit(HashMap<String, Object> data)
I can't use Patch, long story.
I need to document what parameters are possible for client to send up so they can see them in swagger UI. One way to do this is to use #ApiImplicitParam
#ApiImplicitParams({
#ApiImplicitParam(name = "payload", value = "payload", required = true, dataType = "com.me.PartialUpdatePerson", paramType = "body"),
})
This is a nice work around. PartialUpdatePerson is a class which contains the various parameters allowed to be sent up.
The problem is in the swagger UI, it still shows me the body with the map. Sample value is:
{
"additionalProp1": {},
"additionalProp2": {},
"additionalProp3": {}
}
as well as the payload.
Is there anyway, I can tell swagger, not to show this? i.e. to ignore the HashMap data from a doc perspective? I am not using Spring so #ApiIgnore is not possible.
Thanks
public Response edit(#ApiParam(hidden = true) HashMap<String, Object> data)

How to make Feign POST request without a request body and with query params?

I am using Feign with the Apache Http Client and I would like to support the following jax-rs interface:
#POST
#Path("/do_something")
void doSomething(#QueryParam("arg") String arg);
But, ApacheHttpClient uses a RequestBuilder, which converts query parameters for requests without a body/entity into a UrlEncodedFormEntity.
I am converting my APIs to jax-rs, and I do not want to break backwards compatibility. Is there a way to use Feign without adjusting my API? Will the OkHttp or Ribbon clients support POSTs with query params and no body/entity? Is there another java jax-rs client that will support this?
Also, is there a reason why RequestBuilder turns query params into a UrlEncodedFormEntity? Is there an alternative HttpUriRequest builder within the apache-httpclient library that doesn't do this? RequestBuilder's build method has the following lines of code:
if (entity == null && (HttpPost.METHOD_NAME.equalsIgnoreCase(method) || HttpPut.METHOD_NAME.equalsIgnoreCase(method))) {
entity = new UrlEncodedFormEntity(parameters, HTTP.DEF_CONTENT_CHARSET);
} else {
// omitted expected behavior
}
Before switching to Feign, my code constructed a HttpUriRequest with something similar to the following:
URI uri = new URIBuilder()
.setScheme("https")
.setHost("localhost")
.setPath("service/do_something")
.addParameter("arg", "value")
.build();
HttpUriRequest request = new HttpPost(uri);
If you are willing to break the API slightly and maintain support for the #QueryParam, then you could define a request interceptor on the feign client that adds a plain text entity/body to the request:
.requestInterceptor(template -> {
if (template.method().equals(HttpPost.METHOD_NAME) && template.queries().keySet().size() > 0 && template.body() == null) {
template.body(" ");
}
})
Then, your API would change with the following:
#POST
#Consumes(MediaType.TEXT_PLAIN)
#Path("/do_something")
void doSomething(#QueryParam("arg") String arg);
But, this breaks the API since the server now expects/consumes a POST message with a plain text entity/body.
I think the same could be accomplished without the requestInterceptor and with Feign's #Body template:
#POST
#Consumes(MediaType.TEXT_PLAIN)
#Body(" ")
#Path("/do_something")
void doSomething(#QueryParam("arg") String arg);
But, this means that your API would have to include Feign rather than pure jax-rs annotations.

Getting Bad request Error for Ajax call in Spring MVC

I am getting Bad Request Error for my ajax call in Spring MVC. Please refer the code below and kindly comment on this :
ajax call :
$.getJSON('deletRowRequest', {ticketId: ids}, function(data){
alert(data);
});
controller :
#RequestMapping(value="/deletRowRequest", method = RequestMethod.GET)
public #ResponseBody List deleteRow(Model model, #RequestParam(value="ticketId") String ticketId){
String[] ticketString = ticketId.split(",");
String flag = "deleteRow";
List deleteTicketList = new ArrayList();
for(String tick :ticketString){
deleteTicketList.add(tick);
}
System.out.println("list>> "+deleteTicketList);
UpdateDB updatedb = new UpdateDB();
updatedb.updateTable(deleteTicketList, flag);
List ticket = updatedb.getRecordsFromDB();
System.out.println(ticket);
return ticket;
}
I have also included Jackson 1.7.4 jars in the lib folder and <mvc:annotation-driven/> in my servlet.
Please guide me solve this problem.
You're telling String that ticketId is a request param. So spring search for a param in the query string with the name ticketId.
But you're sending ticketId as part of the body of the request.
You have 2 optinos.
Change the invokation, and include your ticketid as part of the querystring.
Change the Spring method, so it could receive the ticketid as part of the body request. You can use #RequestBody instead of #RequestParam.

How to set up Web API Routing for a Proxy Controller?

Part of my application needs to act as a Proxy Server for a third party RESTful web service. Is there a way to set up Web API routing so that all requests of the same type will go to the same method?
For example, if the client sends in either of these GET requests I want them to go into a single GET action method that then sends on the request to the downstream server.
api/Proxy/Customers/10045
api/Proxy/Customers/10045/orders
api/Proxy/Customers?lastname=smith
The single action method for GET would pick up any one of these three requests and send them on to the respective service (I know how to work with HttpClient to make that happen effectively):
http://otherwebservice.com/Customers/10045
http://otherwebservice.com/Customers/10045/orders
http://otherwebservice.com/Customers?lastname=smith
I don't want to have to tightly couple my web service to the third party web service and replicate their entire API as method calls inside mine.
One workaround that I have thought of is to simply encode the target URL in JavaScript on the client and pass this into the Web API which will then only see one parameter. It would work, but I'd prefer to use the routing capabilities in Web API if possible.
Here's how I got this to work. First, create a controller with a method for each verb you want to support:
public class ProxyController : ApiController
{
private Uri _baseUri = new Uri("http://otherwebservice.com");
public async Task<HttpResponseMessage> Get(string url)
{
}
public async Task<HttpResponseMessage> Post(string url)
{
}
public async Task<HttpResponseMessage> Put(string url)
{
}
public async Task<HttpResponseMessage> Delete(string url)
{
}
}
The methods are async because they're going to use an HttpClient. Map your route like this:
config.Routes.MapHttpRoute(
name: "Proxy",
routeTemplate: "api/Proxy/{*url}",
defaults: new { controller = "Proxy" });
Now back to the Get method in the controller. Create an HttpClient object, create a new HttpRequestMessage object with the appropriate Url, copy everything (or almost everything) from the original request message, then call SendAsync():
public async Task<HttpResponseMessage> Get(string url)
{
using (var httpClient = new HttpClient())
{
string absoluteUrl = _baseUri.ToString() + "/" + url + Request.RequestUri.Query;
var proxyRequest = new HttpRequestMessage(Request.Method, absoluteUrl);
foreach (var header in Request.Headers)
{
proxyRequest.Headers.Add(header.Key, header.Value);
}
return await httpClient.SendAsync(proxyRequest, HttpCompletionOption.ResponseContentRead);
}
}
The URL combining could be more sophisticated, but that's the basic idea.
For the Post and Put methods, you'll also need to copy the request body
Also please note a HttpCompletionOption.ResponseContentRead parameter passed in SendAsync call, because without it, ASP.NET will spend an exremeley long time reading the content if the content is large (in my case, it changed a 500KB 100ms request into a 60s request).

Serialization error in service stack when using client library

I have a ServiceStack REST service (PUT and POST) which I have tested with fiddler and if no errors are raised I return
new HttpResult(HttpStatusCode.OK);
Now I am testing the same REST service with the service stack client, I have:
var client = new XmlServiceClient("url"));
client.Post<ChangeServerLicenseDto>("", new ChangeServerLicenseDto()
{ServerName = model.ServerName});
and I get the exception on the REST service when I do
return new HttpResult(HttpStatusCode.OK)
and the error raised is :
500 (Error in line 1 position 76. Expecting element 'ChangeServerLicense'
from namespace ''.. Encountered 'Element' with name 'HttpStatusCode',
namespace 'http://schemas.datacontract.org/2004/07/System.Net'.)
My client code is in a MVC action method (POST).
My datacontract for the RestService is :
[DataContract(Name = "ChangeServerLicense", Namespace = "")]
[RestService("url", "POST", "application/xml")]
public class ChangeServerLicenseDto
{
[DataMember(Name = "ServerName", Order = 1)]
public string ServerName { get; set; }
}
The convention of signalling a successful response is to return an empty Response DTO (which by default returns a 200 OK). Also Send<TResponse>(...) does a POST so if you don't want to include the url in the request, use Send which will POST the request to the automatic pre-defined routes:
var client = new XmlServiceClient("url"));
client.Send<ChangeServerLicenseDtoResponse>(
new ChangeServerLicenseDto {ServerName = model.ServerName});
Otherwise if you still want to use .Post<T>(...) include the URL for the custom route where your services is mounted.
Note: I generally dislike using Dto suffixes on DTOs which are the most important API in your service - I explain in a bit more detail why here.

Resources