Handling un-mapped Rest path - java

This is my current Guice configuration:
public class MyServletModule extends ServletModule {
#Override
protected void configureServlets() {
bind(MyRest.class);
serveRegex(".+(?<!\\.(html|css|png|jpg))")
.with(HttpServletDispatcher.class);
}
}
However I want that my Rest resource is only access in form of http://127.0.0.1:8888/{hashcode_or_filename} and the only form accepted and processed (well, plus the /create method below).
Right now, I can deal with hashcode and filename properly in this path pattern.
However I am not sure how to deal the kind or scenario below, where the client is requesting path that is not mapped, which returns this in my case:
Could not find resource for relative : /examples/foo of full path:
http://127.0.0.1:8888/examples/foo
or
Could not find resource for relative : /examples/bar/foo of full
path: http://127.0.0.1:8888/examples/bar/foo
What I need is to be able to be able to handle unmapped paths so I can return a error HTML page or something and not show these error text in the browser.
Also if the request is: http://127.0.0.1:8888/ I need to forward to http://127.0.0.1:8888/index.html automatically. As right now I have to manually put the index.html in the tail.
My Resteasy resource is configure or wired with just:
#Singleton
#Path("/")
public class MyRest {
#GET
#Path({hashcode})
public Response getSomething(...){}
#POST
#Path("create")
public Response createSomething(...){}
}

Easiest way is to register filter to handle responses with error code other that 200 (OK). Or add to your web.xml something like this:
<error-page>
<error-code>404</error-code>
<location>/ErrorPage.jsp</location>
</error-page>
Also if the request is: http://127.0.0.1:8888/ I need to forward to
http://127.0.0.1:8888/index.html automatically. As right now I have to
manually put the index.html in the tail.
You can use this module http://tuckey.org/urlrewrite/
WEB-INF/web.xml
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
<init-param>
<param-name>confPath</param-name>
<param-value>/WEB-INF/urlrewrite.xml</param-value>
</init-param>
<!--...omitted...-->
</filter>
<filter-mapping>
<filter-name>UrlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
WEB-INF/urlrewrite.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite
PUBLIC "-//tuckey.org//DTD UrlRewrite 4.0//EN"
"http://www.tuckey.org/res/dtds/urlrewrite4.0.dtd">
<urlrewrite>
<rule match-type="regex">
<from>^/$</from>
<to type="redirect">/index.html</to>
</rule>
</urlrewrite>

Related

Prevent Tomcat from interfering with Jersey/JAX-RS 2 Response Body on HTTP Error Status 4xx or 5xx

I have the following stack for a REST API
Jersey 2/JAX RS 2.0
Tomcat 7.0.47
Jackson 2
My goal is to have a custom response body when an error occurs. I want to be able to send the client an explanation what exactly went wrong for easier debugging.
First I tried to use #Context HttpServletResponse and set the http status code there, but it was ignored by jersey (which is the normal behaviour but this is beyond my understanding)
Then I tryed to use WebApplicationException like this:
#GET
#Path("/myapi")
public BaseResponse getSomething() {
BaseResponse b = new BaseResponse();
if(error) {
b.setStatusDescription("reason for error");
throw new WebApplicationException(Response.status(Response.Status.CONFLICT).entity(b).build());
}
//add content to BaseReponse
return b
}
But Tomcat returns me somthing like this:
<html>
<head>
<title>Apache Tomcat/7.0.47 - Error report</title>
<style>
<!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22p
Which is the standard Tomcat html output capped by the contet-length of response body I wanted to return (.entity(b) - the length of b). So it is recognized but Tomcat just overwrites it with its own error page.
As a side note I also tried to just return the Response with the same outcome:
return Response.status(Response.Status.CONFLICT).entity(b).build()
So how do I tell Tomcat to leave me alone and let my own responses through?
Just add the following code in the configuration of ResourceConfig class.
property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, "true");
See jersey.config.server.response.setStatusOverSendError
The problem was that I was using the GzipServlet from ehcache which seems to not work porperly with jersey. During the multiple phases the response wrapper threw exceptions.
This is the dependency of ehcache:
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-web</artifactId>
<version>2.0.4</version>
</dependency>
And the problematic servlet definition in web.xml
<filter>
<filter-name>GzipFilter</filter-name>
<filter-class>net.sf.ehcache.constructs.web.filter.GzipFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>GzipFilter</filter-name>
<url-pattern>/rest/*</url-pattern>
</filter-mapping>
As an alternative Im using Jetty Servlet now: http://download.eclipse.org/jetty/stable-9/apidocs/org/eclipse/jetty/servlets/GzipFilter.html
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId>
<version>8.1.0.RC5</version>
</dependency>
web.xml
<filter>
<filter-name>GzipFilter</filter-name>
<filter-class>org.eclipse.jetty.servlets.GzipFilter</filter-class>
<init-param>
<param-name>mimeTypes</param-name>
<param-value>text/html,text/plain,text/xml,application/xhtml+xml,text/css,application/javascript,image/svg+xml</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>GzipFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
As disclaimer: yes I know I can activate gzip in tomcat config, but Im writing a testserver that must be able to have endpoints with and without gzip.

Jersey REST webservice subresources not found

At first in my web server I only had one REST servlet. Something like:
#Path("/")
public class Controller {
#GET
#Produces({ MediaType.TEXT_HTML })
public Response get(#Context UriInfo info) throws Exception {
...
}
#GET
#Path("resource1")
#Produces({ MediaType.TEXT_HTML })
public Response resource1() throws Exception {
...
}
...
}
And the web.xml:
<servlet>
<servlet-name>rest</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>xpto.mypack1;xpto.mypack2</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>rest</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
But then I wanted to add some static html to the server, so I updated the servlet mapping to
/rest/*
and the #Path directive of Controller servlet class from "/" to "/rest". Everything works fine but the sub-resources or methods of controller with the #path directive that stopped working.. ie:
/ works fine since I have an index.html page at root
/rest works fine, it invokes the get method of my servlet
/rest/resource1 returns 404 http code...
Any help? I already tried a list of combinations of / after and before each #Path directive, with no success... Many thanks
One update:
I used the trace util and got the following results:
for /[app-name]/rest (it works):
X-Jersey-Trace-002 accept right hand path java.util.regex.Matcher[pattern=/rest(/.*)? region=0,11 lastmatch=/rest]: "/rest" -> "/rest" : ""
X-Jersey-Trace-003 accept resource: "rest" -> #Path("/rest") xpto.mypack.Controller
X-Jersey-Trace-000 accept root resource classes: "/rest"
X-Jersey-Trace-001 match path "/rest" -> "/application.wadl(/.)?", "/rest(/.)?"
for /[app-name]/rest/resource1 (it doesn't work):
X-Jersey-Trace-002 matched exception mapper: com.sun.jersey.api.NotFoundException#4fd41dc3 -> xpto.myclass
X-Jersey-Trace-003 mapped exception to response: com.sun.jersey.api.NotFoundException#4fd41dc3 -> 404 (Not Found)
X-Jersey-Trace-000 accept root resource classes: "/resource1"
X-Jersey-Trace-001 match path "/resource1" -> "/application.wadl(/.)?", "/rest(/.)?"
I hope it helps someone to help me..
If you define your servlet mapping as /rest/*, don't repeat /rest in the #Path annotation of your resources. I.e. all you need to do is keep the controller as is (in your question above) and just change the servlet mapping. The URL at which the resources are available is:
<application_context_path>/<servlet_mapping>
So, if you change the #Path annotation from #Path("/") to #Path("rest") and you also change the servlet mapping to /rest, then your resources would be available at:
<application_context_path>/rest/rest/*

How to allow access to static content when having default servlet

I map all requests to /* to a specific servlet.
My static content is hidden by this configuration.
How can i allow access to specific files (such as crossdomain.xml)?
When you map /* to a specific servlet, all requests will be forwarded to that servlet, unless you provide a more explicit mapping to another servlet.
That is, if you have /* mapped to ServletA, and /static/* mapped to ServletB, then following Servlets will get called.
http://localhost:8080/abc.jpg -> ServletA
http://localhost:8080/static/abc.jpg -> ServletB
http://localhost:8080/xyz/abc.jpg -> ServletA
So one option you have is to write a Servlet to handle the static content, which will grab the file and return it as response. You can map that servlet to a prefixed by something like /static/*. This requires that all URL references to your static files to be updated to contain this '/static' part.
If that is not feasible for you, then probably you can use the same servlet, but mapped to multiple URL patterns (probably by extension) as follows.
<servlet>
<servlet-name>static-servlet</servlet-name>
<servlet-class>xxx.yyy.StaticServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>static-servlet</servlet-name>
<url-pattern>*.xml</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>static-servlet</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
If you want this to be fine-grained to the level of each file, you can map the servlet to your file URL as well.
Cookbook:
Map your controller Servlet on a more specific url-pattern like /app/*.
Put all the static content in a specific folder like /static.
Create a Filter which is mapped on /* which transparently continues the chain for any /static requests and dispatches other requests to /app.
So, in a nutshell:
<filter>
<filter-name>filter</filter-name>
<filter-class>com.example.Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>controller</servlet-name>
<servlet-class>com.example.Controller</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>controller</servlet-name>
<url-pattern>/pages/*</url-pattern>
</servlet-mapping>
with the following in filter's doFilter():
String uri = ((HttpServletRequest) request).getRequestURI();
if (uri.startsWith("/static/")) {
chain.doFilter(request, response); // Goes to default servlet.
} else {
request.getRequestDispatcher("/app" + uri).forward(request, response);
}
No, you do not end up with extra /app path in the URL. It's fully transparent. Make if necessary "/static" and/or "/app" an <init-param> of the filter.
And one more(a direct) servlet mapping like this<servlet-mapping><servlet-name>StaticContentServlet</servlet-name><url-pattern>/crossdomain.xml</url-pattern></servlet-mapping>
probably you can put your static content under different URL like /static/* and then map this URL to a Servlet which responds with the static content.

Accessing context parameters within Servlet Filters

Thanks to everyone in advance,
I am trying to access any context parameters in the web.xml from within a servlet filter. Below is a portion from my web.xml file. I have verified that the context-param node is accessible via a jsp page using out.print(getServletContext().getInitParameter("error"));.
<filter>
<filter-name>prePost</filter-name>
<filter-class>MyFilter</filter-class>
<init_param>
<param_name>error</param_name>
<param_value>/test.jsp</param_value>
</init_param>
<filter-mapping>
<filter-name>prePost</filter-name>
<url-pattern>*.jsp</url-pattern>
<context-param>
<description>Error Handler</description>
<param-name>error</param-name>
<param-value>/test.jsp</param-value>
In my filters doFilter when I output this.filterConfig.getInitParameter("error"), I always get null. In my filters init() I am setting this.filterConfig with the passed in FilterConfig.
Thanks,
Sam
You're using underscores rather than hyphens for "param-name" and "param-value". Your config should look like this:
<init-param>
<param-name>error</param-name>
<param-value>/test.jsp</param-value>
</init-param>

How to remove the file suffix/extension (.jsp and .action) using the Stripes Framework?

I'm looking to use pretty / clean URL's in my web app.
I would like the following URL:
http://mydomain.com/myapp/calculator
.. to resolve to:
com.mydomain.myapp.action.CalculatorActionBean
I tried overwriting the NameBasedActionResolver with:
public class CustomActionResolver extends NameBasedActionResolver {
public static final String DEFAULT_BINDING_SUFFIX = ".";
#Override
protected String getBindingSuffix() {
return DEFAULT_BINDING_SUFFIX;
}
#Override
protected List<String> getActionBeanSuffixes() {
List<String> suffixes = new ArrayList<String>(super.getActionBeanSuffixes());
suffixes.add(DEFAULT_BINDING_SUFFIX);
return suffixes;
}
}
And adding this to web.xml:
<servlet-mapping>
<servlet-name>StripesDispatcher</servlet-name>
<url-pattern>*.</url-pattern>
</servlet-mapping>
Which gets me to:
http://mydomain.com/myapp/Calculator.
But:
A stray "." is still neither pretty nor clean.
The class name is still capitalized in the URL..?
That still leaves me with *.jsp..? Is it even possible to get rid of both .action and .jsp?
I think you are looking for the #URLBinding annotation. Look at #URLBinding on your Bean.
#UrlBinding("/calculator")
Try to use DMF
http://stripes.sourceforge.net/docs/current/javadoc/net/sourceforge/stripes/controller/DynamicMappingFilter.html
I was trying to do the same thing, and had the same question, though I wanted my URL to use the trailing slash http://mydomain.com/myapp/calculator/
The answer is to use #UrlBinding & the DynamicMappingFilter
I modified the example to have:
#UrlBinding("/calculator/")
public class CalculatorActionBean implements ActionBean {
.
.
.
return new ForwardResolution("/WEB-INF/view/calculator.jsp");
Then I added the DMF to web.xml:
<filter>
<display-name>Stripes Dynamic Mapping Filter</display-name>
<filter-name>DynamicMappingFilter</filter-name>
<filter-class>net.sourceforge.stripes.controller.DynamicMappingFilter</filter-class>
<init-param>
<param-name>ActionResolver.Packages</param-name>
<param-value>com.example.stripes</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>DynamicMappingFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
Now the clean URL works as expected, and I'm never redirected to a *.action URL after interacting with the form.

Categories

Resources