I am developing a JAX-RS REST service. I'm using Java 1.8, Tomcat 7, and invoking my POST through JQuery/AJAX.
My JQuery code is very simple:
$.post(ctx + "/addressinfo/", data, null, "json")
where data is a simple JSON object containing basic address information.
My REST controller is as follows:
package addressservice.controller;
import javax.servlet.*;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.goodyear.menuadmin.plantservice.model.PlantInfo;
import com.goodyear.menuadmin.plantservice.service.PlantInfoService;
#Path("/")
public class AddressController {
private AddressService service = new AddressService ();
private Address address;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getAddress(#Context ServletContext ctx) throws Exception {
this.address= plantService.getInfo("Name");
return Response.ok(this.plant, MediaType.APPLICATION_JSON).build();
}
#POST
#Consumes(MediaType.MEDIA_TYPE_WILDCARD)
private Response saveAddress(Address data) {
boolean isOk = addressService.updateInfo(data);
if(!isOk) {
Response.serverError().build();
}
return Response.ok(data, MediaType.APPLICATION_JSON).build();
}
}
However, when I execute this, I am receiving a 405 Method Not Allowed error. The post is initiated from a button click on a form.
The GET portion works just fine.
The response headers are:
HTTP/1.1 405 Method Not Allowed
Server: Apache-Coyote/1.1
Allow: HEAD,GET,OPTIONS
Content-Length: 0
Date: Mon, 10 Sep 2018 17:58:08 GMT
I've tried several different attempts to correct this ranging from adding this init parameter to my tomcat web.xml
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
I've tried changing from $.post to the following:
return $.ajax({
url: ctx + "/addressinfo/",
type: 'post',
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify(data)
});
EDIT
I neglected to add the servlet definition for the application's web.xml
<servlet>
<servlet-name>Address Service</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>addressservice.controller</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Plant Service</servlet-name>
<url-pattern>/addressinfo/*</url-pattern>
</servlet-mapping>
Nothing seems to be allowing me to post to my REST service.
Is there something I need to change in my Tomcat config? Or Am I doing something else wrong? I suppose I could change from a REST service to a servlet, But I would prefer to not have to do that.
Your Java method for #POST is private, it should be public. Jersey ignores private methods in Controllers.
saveAddress only consumes data doesn't produce
so implement
#Produces
in saveAddress as well
Related
My application
In my JAX-RS/Jersey application running in Tomcat, I need to run a filter class that implements ContainerRequestFilter before every resource method. The filter determines whether or not the service is available right now1. This is done rather simply if I register the filter in web.xml:
DataResource.java:
package thepackage;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
#Path("/data")
public class DataResource {
#GET
#Produces("application/json")
public String getData() {
return "{\"data\": [{\"id\": 1},{\"id\": 2}]}";
}
}
ServiceAvailabilityFilter.java:
package thepackage;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerRequestFilter;
import java.util.Random;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
public class ServiceAvailabilityFilter implements ContainerRequestFilter {
private static final Random RANDOM = new Random();
private boolean isServiceAvailable(ContainerRequest request) {
// Actual logic goes here; for this sample, just flip a coin
return RANDOM.nextBoolean();
}
#Override
public ContainerRequest filter(ContainerRequest request) {
if (isServiceAvailable(request)) {
return request;
} else {
throw new WebApplicationException(
Response.serverError().type(MediaType.APPLICATION_JSON_TYPE)
.entity("{\"error\":\"Service not available\"}").build()
);
}
}
}
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>jersey</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>io.github.garysheppardjr.jersey1xfiltertest</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>io.github.garysheppardjr.jersey1xfiltertest.ServiceAvailabilityFilter</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
My question
However, I don't want to declare the ServiceAvailabilityFilter in XML. I want to register it in Java code. How would I do that?
Things I would rather not do
Declare the filter in web.xml. Why not? If I deliver this application as on-premises software, I want to make sure someone can't disable my filter by editing web.xml.
Check for service availability in the DataResource class. Why not? I don't want to write and maintain new code in all of my resource classes. This is one thing that filters can avoid.
Change from Jersey 1.x to Jersey 2.x. Why not? I have dependencies and deadlines that will make moving to Jersey 2.x very difficult.
1 Why might the service be unavailable, you might ask? It doesn't matter. Use your imagination. That logic is not relevant to this question, so I have replaced it with Random.nextBoolean().
Remove your <servlet> element from web.xml (or even delete web.xml altogether if that's all you have in it and you're using at least Servlet API 3.0). To take the place of the <servlet> element, create a class that extends javax.ws.rs.core.Application. In the case of this question, it might be easiest to extend com.sun.jersey.api.core.PackagesResourceConfig. Annotate your new class with javax.ws.rs.ApplicationPath. Do your <init-param> settings in the constructor of this class.
DataResource.java: no change.
ServiceAvailabilityFilter.java: no change.
MyApplication.java (or whatever you want to call it):
package thepackage;
import com.sun.jersey.api.core.PackagesResourceConfig;
import javax.ws.rs.ApplicationPath;
#ApplicationPath("/")
public class MyApplication extends PackagesResourceConfig {
public MyApplication() {
// "com.sun.jersey.config.property.packages" init-param
super(DataResource.class.getPackageName());
// "com.sun.jersey.spi.container.ContainerRequestFilters" init-param
getContainerRequestFilters().add(ServiceAvailabilityFilter.class);
}
}
web.xml: delete at least the <servlet> element, or you can delete the file altogether.
I have created a simple restful web service based on some examples and built it into a .war file(project structure has web.xml under WEB-INF), deploy it on glassfish ang get a 404 not found error when i try to call it.
My class containing the service is :
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
/**
* Created by Nikos Kritikos on 10/22/2015.
*/
#Path("/decks")
public class HS_Services {
#Path("sayHello/{name}")
#GET
public String doSayHello(#PathParam("name") String name) {
return "Hello there "+name;
}
}
my web.xml is this :
<servlet>
<servlet-name>HSRestServices</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HSRestServices</servlet-name>
<url-pattern>/hsrest/*</url-pattern>
</servlet-mapping>
i try to call it with http://localhost:8080/HSRestServices/hsrest/decks/sayHello/Nikos but i get 404 from glassfish..
Any help would be much appreciated, thank you.
You are missing the init-param where you specify which packages should be scanned for REST endpoint classes.
Change your web.xml to look like this:
<servlet>
<servlet-name>HSRestServices</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>insert.packagename.where.your.class.is.here</param-value>
</init-param>
</servlet>
Make sure to insert the name of the package which contains your class.
You don't need Spring for this.
There is also another way which works without web.xml. For details have a look at this question.
Firstly I would like to say that I am new to development and have started working directly with REST. So I may be stupid to ask some silly questions. Please accept my apologies in advance!
Regarding the Problem - I am trying to learn how REST API WebServices can be developed so I started with a Mavan Project using the Archetype - "jersey-quickstart-webapp" and using Chrome POSTMAN as the Rest Client.
I am successful in writing GET methods but getting struck while writing PUT.
Every time I get only two things - a) Runtime Exception with 404 error or b) 415 Error with Media unsupported.
I have tried different combinations for #Consumes annotation and method parameters like #FormParam, #QueryParam but no luck. I am neither successful in reading the value sent via PUT to my application from POSTMAN nor I am able to send a simple response back to POSTMAN. Please suggest and drive me a path where I can overcome these small hurdles.
My code -
package tcs.suraj.learnwebservices;
import java.util.ArrayList;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import tcs.suraj.learnwebservices.domain.MovieBean;
#Path("/movies")
public class Movies {
static ArrayList<MovieBean> movieList = new ArrayList<MovieBean>() ;
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getMovie(){
System.out.println("Under Construction");
return "Under Construction";
}
#PUT
#Consumes("application/x-www-form-urlencoded")
#Produces(MediaType.TEXT_PLAIN)
public String updateMovie(#FormParam("name") String name){
Response r ;
System.out.println(name +" updated!");
return name;
}
}
I am assuming you are using Tomcat for deployment, add this to your web.xml and then try again.
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.methods</param-name>
<param-value>GET,POST,HEAD,OPTIONS,PUT,DELETE</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Note: I have used org.apache.catalina.filters.CorsFilter in Tomcat 7.0.53. I am not sure what is the least version supported for CorsFilter.
I want to build a REST-API with Jax-Rs with cors enabled. So I googled how to do and found this:
http://www.developerscrappad.com/1781/java/java-ee/rest-jax-rs/java-ee-7-jax-rs-2-0-cors-on-rest-how-to-make-rest-apis-accessible-from-a-different-domain/
The solution is a ResponseFilter, that adds some header information to every response, so that the browser of the user knows, that cross domain accesses are allowed.
Because the ResponseFilter is not executed when I do any request (tried GET, POST and OPTIONS), I googled again and found this:
ContainerResponseFilter not working
#lefloh gave a good answer, that sounds logic (to remove the annotation #PreMatching). It did so, but nevertheless my filter is not invoked, when I do a Http-Request.
This is my code:
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
#Provider
public class RESTServiceResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext parContainerRequestContext, ContainerResponseContext parContainerResponseContext) throws IOException {
parContainerResponseContext.getHeaders().add( "Access-Control-Allow-Origin", "*" );
parContainerResponseContext.getHeaders().add( "Access-Control-Allow-Credentials", "true" );
parContainerResponseContext.getHeaders().add( "Access-Control-Allow-Methods", "GET, POST, DELETE, PUT" );
parContainerResponseContext.getHeaders().add( "Access-Control-Allow-Headers", "Content-Type" );
}
}
I kept on googling and found out, that I forgot to add the Filter to the web.xml. So I also did that:
<servlet>
<display-name>webinterface.api</display-name>
<servlet-name>JAX-RS REST Servlet</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>de.tsystems.lbus.apprestserver</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>de.apprestserver.filter.RESTServiceResponseFilter</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JAX-RS REST Servlet</servlet-name>
<url-pattern>/TNR/*</url-pattern>
</servlet-mapping>
I have no more ideas now and want to ask you, if you maybe have a solution for me. Thanks in advance!
You're using Jersey 1 (can tell by the com.sun.jersey in your web.xml). The filter implementation you are using (or showing us) is Jersey 2. There's subtle difference, but it's a major difference. The latter will not work with Jersey 1.
And the fact that it even compiles (if that's the case) means that you need to get rid of some dependencies. The class you have is a JAX-RS 2 class (interface). Any JAX-RS/Jersey 2 dependency, you might have, get rid of them. They don't play well (maybe not cause of issue, but get rid of them to drop any confusion)
Jersey 1 == com.sun.jersey (keep)
Jersey 2 == org.glassfish.jersey (get rid of)
JAX-RS 2 api == javax.ws.rs-api (get rid of)
See here for Jersey 1 implementation and configuration
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.