RESTful web services using jQuery ajax - java

I am using restful web services using java
#Path("/data")
public class StudentDataService {
StudentInfo st=new StudentInfo();
#GET
#Path("/mydata")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public List<Student> findAll() {
System.out.println("data returned to the web service");
return st.populateData();
}
}
I am returning a list of data containing student name,id,marks, etc.
trying to call this method with jQuery ajax as follows:
function findAll() {
console.log('findAll');
$.ajax({
type: "GET",
url: "http://localhost:9297/StudentData/rest/data/mydata",
dataType: "json",
crossDomain: true,
success: function(resp){
// we have the response
alert("Data\n '" + resp + "'");
},
error: function(e){
alert('Error: ' + e);
}
});
}
I am getting error
also it is giving error on console like "no elements found"

"getting exception: javax.ws.rs.WebApplicationException: com.sun.jersey.api.MessageException: A message body writer for Java class java.util.ArrayList, and Java type java.util.List<dao.Student>, and MIME media type application/json was not found""
You seem to missing a JSON provider. If you are using Maven, you can add this dependency
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.4.0</version>
</dependency>
If you're not using Maven, go look for these jars
You can search for and download all the jars here
Then you can register the provider, by adding the Jackson package to your packages to scan (in your web.xml configuration)
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>
your.packages.to.scan,
com.fasterxml.jackson.jaxrs.json
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

Use javax.ws.rs.core.Response object and build the output. Else have your own wrapper class and wrap your output and return the wrapper class back to response.

I recommend you to use gson for working with json on server side, because it's very easy to use. For example:
List<Student> items = new ArrayList<>();
while (...) {
items.add(new Student(...));
}
Gson gson = new Gson();
return gson.toJson(items);
This is just an example, adopt this to your requirements.

You might be running into a cors issue. Since you're trying to get type application/json back from the server and your request is crossDomain. If you wrap your jax-rs method with a Response object like #user1129947 suggested, you can modify the headers of the response to allow a cross-origin request like so:
import javax.ws.rs.core.Response;
#Path("/data")
public class StudentDataService {
StudentInfo st=new StudentInfo();
#GET
#Path("/mydata")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response findAll() {
return Response.ok(st.populateData())
.header("Access-Control-Allow-Origin", "*")
.build();
}
}
The header Access-Control-Allow-Origin: * will now be on the response which will tell the browser that it is allowed to receive application/json data.
Please be aware that the * parameter in the header allows any domain to access this resource
If you want to be sure that only your domains can access this resource then you must set the value of the header to a specific domain.
If you use custom headers or you end up wanting to send cookies to the server then you'll have to deal with preflighted requests which is beyond the scope of this answer.
See Mozilla's documentation on CORS for more information
Bonus Edit:
For anyone using CDI with JAX-RS you can check out this filter-interceptor combo for dealing with CORS

Related

How to allow cross origin request with java spring

I have tried everything. i dont know how to do it. it just doesent work.
I have added "#CrossOrigin" / "#CrossOrigin(maxAge = 3600)" practicaly everywere.
how do i configure java spring to allow cross-origin requests?
This is how my request looks like
$.ajax({
crossOrigin: true,
url: "http://localhost:80/adData",
type: "GET",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (responseSchools) {
//stuff
},
error: function (request, status, error) {
//stuff
}
});
Code of Java app
#GetMapping
#CrossOrigin
#RequestMapping(value = AD_DATA_ENDPOINT)
public String getAdData() {
return "wuwuwu";
}
A rather pedestrian, but working approach is to overwrite the header from within the handling method:
// remember to add an `HttpServletResponse response` parameter
response.setHeader("Access-Control-Allow-Origin", "*");
This however is a per-handler solution. On the bright side, it works for any and all headers: whatever Spring was going to do for you, you can always overwrite it in the handler itself.
After searching a lot for this I found the solution for this:
There is no problem in the code, since you are using Cross Origin request it first sent OPTIONS request to the server. In order to fix this I added following piece of code in my server configuration section:
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
Also create a file with following code:
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
I am sure this will solve your problem ;)
First of all, most servers allow GET requests from everywhere. Second, there are multiple ways of handling CORS requests on your server.
#CrossOrigin annotation (which allows GET, HEAD a d POST methods by default) can be placed on your Controllers or your Controller Class.
A CORS Filter can be used which will allow OPTIONS requests.
Using WebMvConfigurer, you can specify your configuration.
Find more details here : https://spring.io/guides/gs/rest-service-cors/
Add below maven dependency
<!-- https://mvnrepository.com/artifact/com.thetransactioncompany/cors-filter -->
<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>cors-filter</artifactId>
<version>2.6</version>
</dependency>
And add below line into web.xml
<filter>
<filter-name>CORS</filter-name>
<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
<init-param>
<param-name>cors.supportedMethods</param-name>
<param-value>GET, POST, HEAD, PUT, PATCH, DELETE, OPTIONS</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CORS</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
this solves my problem.

Tomcat 8 ignores CORS filters (web.xml, in-code, etc)

for some weird reason, Tomcat 8 (on a w2k12 server) is ignoring my CORS filter settings on tomcat8/conf/web.xml which are as follows, according to the Apache Tomcat 8 docs:
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.origins</param-name>
<param-value>*</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
It does work with GET requests, but when I try to do a POST, i get the 403 Forbidden error saying:
Origin http://webapp.mycompany.com is not allowed by Access-Control-Allow-Origin.
Where http://webapp.mycompany.com is my company's hosting provider with standard features you get with a shared hosting with cpanel, my front-end is there and it send the requests to another domain where I have the Tomcat 8 Server installed and the webapp WAR which is a Jersey-based REST Api.
Now, according to the defaults in Apache's official documents, that configuration I'm using is the minimum required for it to work.
I tried then moving the filters to the application's web.xml instead, no luck, tried adding more configurations like methods allowed, tried adding the headers in the responses directly like this:
Response.status(Response.Status.OK)
.entity(relaciona)
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, HEAD")
.header("Access-Control-Allow-Headers", "X-PINGOTHER, Origin, X-Requested-With, Content-Type, Accept")
.header("Access-Control-Max-Age", "1728000")
.build();
Didn't work.
Any help on this would be greatly appreciated, and just to confirm, tried the following:
Tried setting the configuration in both the server and the webapp web.xml files individually, no luck
Tried adding the headers to each response in the Jersey REST Api, no luck
The requests to the API are done with an HTML5 frontend with bootstrap and jQuery, tried adding the crossDomain:true to the ajax requests, no difference.
Added the origin domain to the xml in both web and server web.xml files (tested individually) and neither worked
Thanks!
once you are querying a POST with crossdomain, you need to define #OPTION for #POST /#PUT / #DELETE queries.
Have you defined #Option class in order to define your #Post?
here is an example on how to do so:
#OPTIONS
#Compress
#Path("/mypost")
#Consumes({ MediaType.MULTIPART_FORM_DATA })
#Produces({ MediaType.APPLICATION_JSON })
public Response mypost_opts() {
return Response.ok().build();
}
#POST
#Compress
#Path("/mypost")
#Consumes({ MediaType.MULTIPART_FORM_DATA })
#Produces({ MediaType.APPLICATION_JSON })
public Response mypost() {
return Response.ok().build();
}

Cross-Origin Request Blocked Spring REST service + AJAX

Unable to call spring REST service
My spring service
#RequestMapping(value = "/MAS/authenticate", method = RequestMethod.POST)
public ResponseEntity<Map<String, String>> authenticate(#RequestBody Subject subject) {
Map<String, String> result = new HashMap<String, String>();
result.put("result_detail", "Invalid Password");
result.put("result", "failure");
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
responseHeaders.add("Access-Control-Allow-Origin", "*"); // also added header to allow cross domain request for any domain
return new ResponseEntity<Map<String, String>>(result, responseHeaders, HttpStatus.OK);
}
My AJAX code
$.ajax(
{
crossDomain: true,
type: "POST",
contentType: "application/json; charset=utf-8",
async: false,
url: "http://localhost:8080/SpringMVC/rest/MAS/authenticate",
headers: {"Access-Control-Allow-Origin" : "*"},
data:{},
dataType: "json", //also tried "jsonp"
success: function(data, status, jqXHR)
{
alert('success');
},
error: function(jqXHR, status)
{
alert('error');
}
});
I am getting following error :(
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/SpringMVC/rest/MAS/authenticate. This can be fixed by moving the resource to the same domain or enabling CORS.
i have also tried dataType: "jsonp". its append my body object into URL which make different URL and cannot hit my service URL then and got 404 error.
My browser: firefox 36.0.4
How i can get rid from this error, any help?
My AJAX call and service were OK. After searching a lot on internet i have found that its server side problem not client side.
on server side with Spring we have to implement filter which will allow CORS requests.
filter will look like this.
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.filter.OncePerRequestFilter;
public class CORSFilter extends OncePerRequestFilter {
private static final Log LOG = LogFactory.getLog(CORSFilter.class);
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
response.addHeader("Access-Control-Allow-Origin", "*");
if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
LOG.trace("Sending Header....");
// CORS "pre-flight" request
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
// response.addHeader("Access-Control-Allow-Headers", "Authorization");
response.addHeader("Access-Control-Allow-Headers", "Content-Type");
response.addHeader("Access-Control-Max-Age", "1");
}
filterChain.doFilter(request, response);
}
}
and in web.xml apply this filter on your service requests like this
<filter>
<filter-name>cors</filter-name>
<filter-class>com.test.common.controller.CORSFilter</filter-class> <!-- your package name and filter class -->
</filter>
<filter-mapping>
<filter-name>cors</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
This may help someone else who went through this problem. :)
By default the only method allowed is a GET, and you don't allow the POST on your server side:
Access-Control-Allow-Origin: *
This header only enables CORS, but you need to add this:
Access-Control-Allow-Methods: POST, GET
More detailed how-to about the HTTP access control (CORS) on Mozilla project
So your code should be something like this:
responseHeaders.add("Access-Control-Allow-Methods", "POST, GET"); // also added header to allow POST, GET method to be available
responseHeaders.add("Access-Control-Allow-Origin", "*"); // also added header to allow cross domain request for any domain
Update:
I have re-read the article, and found out some details:
A simple cross-site request is one that:
Only uses GET, HEAD or POST. If POST is used to send data to the
server, the Content-Type of the data sent to the server with the HTTP
POST request is one of application/x-www-form-urlencoded,
multipart/form-data, or text/plain.
Does not set custom headers with
the HTTP Request (such as X-Modified, etc.)
As you can read in bold, you must set other Content-Type for your data (currently it is contentType: "application/json; charset=utf-8",) or use the preflight technique described later:
It uses methods other than GET, HEAD or POST. Also, if POST is used
to send request data with a Content-Type other than
application/x-www-form-urlencoded, multipart/form-data, or text/plain,
e.g. if the POST request sends an XML payload to the server using
application/xml or text/xml, then the request is preflighted.
It sets custom headers in the request (e.g. the request uses a header such as
X-PINGOTHER)
So I suggest you either change the contentType or try to work with this header into your request:
Access-Control-Request-Headers: X-HEADER_NAME_OF_YOUR_CHOOSE
and this headers into your response:
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-HEADER_NAME_OF_YOUR_CHOOSE
And after that you can try to call your method.
Following is the solution for cross platform spring boot web service call.
Application URL: http://localhost:8080
Webservice URL: http://localhost:9090
In your spring controller use following annotation
#CrossOrigin(origins = "http://localhost:8080")
#RequestMapping(value = "/uri", method = RequestMethod.GET)
public SomeObject someMethod(){
// your logic will come here
}

Using JAXB to parse JSON in Jersey

I'm trying to create a very simple RESTful web service using Jersey. I'm trying to make it so that it consumes and produces a JSON by using JAXB. The problem is that I get an error when I pass a JSON to it.
Below is the resource code. Both status() and echo() are working properly. Please note that on processRequest() I'm currently producing a text response, but that will be changed later to produce a JSON.
package web_service;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
#Path("/")
public class WebService {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String status() {
return "Web service is up and running.";
}
#POST
#Consumes(MediaType.TEXT_PLAIN)
#Produces(MediaType.TEXT_PLAIN)
public String echo(String consumed) {
return consumed;
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_PLAIN)
public String processRequest(JAXBElement<Request> r) {
Request request = r.getValue();
return "Latitude: " + request.latitude +
"\n:Longitude: " + request.longitude;
}
}
This is the Request model:
package web_service;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Request {
public String latitude;
public String longitude;
public Request() {}
public Request(String latitude, String longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
// Getters and setters for both
}
My web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID"
version="2.5">
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>web_service</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Finally, this is an example of the POST that I'm doing (headers set to 'Content-Type: application/json');
{
"latitude":"25.764084501106787",
"longitude":"-80.37422332275389"
}
When I run this, Tomcat gives me the following Exception:
WARNING: WebApplicationException cause:
org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=application/json, type=class web_service.Request, genericType=class web_service.Request.
And I get the following response:
415 Unsupported Media Type
Content-Length: 1 kB
Content-Type: text/html;charset=utf-8
Date: 2013 Nov 4 17:41:20
Server: Apache-Coyote/1.1
I'm very new to this and this is not making a lot of sense. Hopefully, one of you will be able to give me a hand. Thanks!
Add this to your web.xml
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
For more details look at jersey documentation : https://jersey.java.net/documentation/1.18/json.html
Try this in your endpoint:
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_PLAIN)
public String processRequest(Coordinates coordinates) {
return "Latitude: " + coordinates.getLatitude() +
"\n:Longitude: " + coordinates.getLongitude();
}
where Coordinates is a simple POJO mirroring the JSON content you are posting.
Then use Jackson, which has JAXB support, by adding all the libraries to your project and adding this configuration:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>org.foobar.rest.services;org.codehaus.jackson.jaxrs</param-value>
</init-param>
to the "Jersey REST Service" servlet.
JAXB (Java Architecture for XML Binding) avaialable in java for binding xml not for JSON.Here you are trying to convert JavaScript Object Notation with the help of xml binder in this situation and that is not possible. And i am pretty sure that you must be getting the error generated by " BodyWriter class " in the JAX-RS package....
Anyway If you want to produce and consume JSON using your resource you gonna need "moxy" JAR available in your project library for handing this conversion :)
Hope what i am writing here will be helpful for other programmers
this error happends when you are requesting without specified headers.
In your service
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_PLAIN)
Ensure that your request gets the header "content-type" ? application/json and the header "accept" contains text/html
I think, there is problem with your Request model class. Your model class is not specifying #XmlaccessType, so by default it is being considered as PUBLIC_MEMBER. As your instance variables are also public along with getters and setters, So JAXB is not able to figure out proper bindings.That's why that exception is coming. So there are two fixes to your problem.
1)You can make your instance variables as private.
2)Or you can explicitly specify #XmlaccessType for your model class and likewise provide annotations in it which are non-conflicting.

Custom HTTP status response with JAX-RS (Jersey) and #RolesAllowed

With my very simple JAX-RS service I'm using Tomcat with JDBC realm for authentication, therefore I'm working the the JSR 250 annotations.
The thing is that I want to return a custom message body in the HTTP status response. The status code (403) should stay the same. For example, my service looks like the following:
#RolesAllowed({ "ADMIN" })
#Path("/users")
public class UsersService {
#GET
#Produces(MediaType.TEXT_PLAIN)
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public String getUsers() {
// get users ...
return ...;
}
}
If a user with a different role than "ADMIN" access the service, I want to change the response message to something like that (depending on the media type [xml/json]):
<error id="100">
<message>Not allowed.</message>
</error>
At the moment Jersey returns the following body:
HTTP Status 403 - Forbidden
type Status report
message Forbidden
description Access to the specified resource (Forbidden) has been forbidden.
Apache Tomcat/7.0.12
How can I change the default message body? Is there a way to handle the (maybe thrown) exception to build my own HTTP status response?
The easiest way to handle this sort of thing is to throw an exception and to register an exception mapper to convert into the kind of message you want to send in that case. So, suppose you throw an AccessDeniedException, you would then have a handler like this (with full class names in places for clarity):
#javax.ws.rs.ext.Provider
public class AccessDeniedHandler
implements javax.ws.rs.ext.ExceptionMapper<AccessDeniedException> {
public javax.ws.rs.core.Response toResponse(AccessDeniedException exn) {
// Construct+return the response here...
return Response.status(403).type("text/plain")
.entity("get lost, loser!").build();
}
}
The way in which you register the exception mapper varies according to the framework you're using, but for Jersey you should be fine with just using #Provider. I'll let you figure out for yourself how you want to generate the kind of error documents that you want, but I do recommend handling failures as HTTP error codes of some kind (that's more RESTful...)
With creating an ExceptionMapper (mapping exceptions of WebApplicationException) it is possible to "catch" certain exceptions thrown by the application:
#Provider
public class MyExceptionMapper implements ExceptionMapper<WebApplicationException> {
#Override
public Response toResponse(WebApplicationException weException) {
// get initial response
Response response = weException.getResponse();
// create custom error
MyError error = ...;
// return the custom error
return Response.status(response.getStatus()).entity(error).build();
}
}
You also need to add the package to your application web.xml for registering the provider:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>
com.myapp.userservice; // semi-colon seperated
com.myapp.mappedexception
</param-value>
</init-param>
REST is build upon HTTP so you don't have to change the default behavior of an authentication failure. Having a 403 error when accessing a resource is enough for the client to clearly understand what appends.
The more your resources are HTTP compliant, the more others can understand it.

Categories