I'm using gretty and running app with tomcat.
My only servlet is:
#WebServlet(name = "frontServlet", urlPatterns = arrayOf("/"))
class FrontServlet : HttpServlet() {
override fun doGet(req: HttpServletRequest, resp: HttpServletResponse) {
Router.doGet(req, resp)
}
...
}
My static assets are under WebContent/public
BUT any request including with paths to assets is handled by FrontServlet.
Default static serving works if I set FronServlet's url pattern to specific one (but I need it to catch all requests except for requests to static files).
What should I do and is there any way to invoke server's default static file handlers from my custom servlets?
Well, after struggling with such ancient technology and using some other answers, I've came to this two solutions:
1. In FrontServlet doGet method:
if (req.requestURI.startsWith("/static/") || req.requestURI.startsWith("/favicon.ico")) {
req.session.servletContext.getNamedDispatcher("default").forward(req, resp)
} else {
Router.doGet(req, resp)
}
Write a filter:
#WebFilter(filterName = "frontFiletr", urlPatterns = arrayOf("/*"))
class FrontFilter: Filter {
override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
val path = (request as HttpServletRequest).requestURI
if (path.startsWith("/static/")) {
chain.doFilter(request, response)
} else {
request.session.servletContext.getNamedDispatcher("frontServlet").forward(request, response)
}
}
}
Related
I have the following controller:
#Controller
#RequestMapping("/my-account")
public class AccountController {
#RequestMapping(value = "/foo/post",
method = RequestMethod.POST)
public String doPost(final RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("flashAttribute", "flashAttributeValue");
return "redirect:/my-account/foo/get";
}
#RequestMapping(value = "/foo/get",
method = RequestMethod.GET)
public void doGet(final HttpServletRequest request, final Model model) {
System.out.println("in request: " + RequestContextUtils.getInputFlashMap(request).get("flashAttribute"));
System.out.println("in model: " + model.asMap().get("flashAttribute"));
}
}
I would also like to access the flash attribute flashAttribute during the invocation of a filter in the filter chain that finally invokes springs default DispatcherServlet which in turn invokes AccountController.
public class FlashAttributeBasedFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain)
throws ServletException, IOException {
String flashAttribute = // how to access the redirectAttribute flashAttribute here?
// do something with flashAttribute ...
filterChain.doFilter(request, response);
}
The DispatcherServlet uses a org.springframework.web.servlet.FlashMapManager that handles these flash attributes, but it doesn't provide read-only access so I think I would be messing something up if I would use it in the filter. And also the FlashMapManager instance is kept in the dispatcher servlet privately.
Does anybody have an idea how I can make the redirect attribute accessible in the filter chain for the GET request succeeding the POST?
Considering that all these methods return null into my filter (I don't understand why):
RequestContextUtils.getFlashMapManager(httpRequest)
RequestContextUtils.getInputFlashMap(httpRequest)
RequestContextUtils.getOutputFlashMap(httpRequest)
I used a drastic solution: read directly the into the session (where flash attributes are stored).
CopyOnWriteArrayList<FlashMap> what = (CopyOnWriteArrayList<FlashMap>) httpRequest.getSession().getAttribute("org.springframework.web.servlet.support.SessionFlashMapManager.FLASH_MAPS");
if (what != null) {
FlashMap flashMap = what.get(0);
[read flashMap as you read a HashMap]
}
I know, this code is super ugly but at the moment I don't find another solution.
Had the same problem, following works for me.
FlashMap flashMap = new SessionFlashMapManager().retrieveAndUpdate(request, null);
flashMap.get("parameter");
I'm trying to integrate a 3rd party servlet into my Spring Boot application and when I try to submit a POST to the servlet, I see the following in the logs:
PageNotFound: Request method 'POST' not supported
I've made a simple test that show this. I started using an auto generated Spring Boot project. Then I created the following Servlet:
public class TestServlet extends HttpServlet {
private static final Logger log = LoggerFactory.getLogger(TestServlet.class);
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp); //To change body of generated methods, choose Tools | Templates.
log.info("doPost was called!!");
}
}
Then I my created Configuration like so:
#Configuration
public class ServletConfig {
#Bean //exposes the TestServlet at /test
public Servlet test() {
return new TestServlet();
}
}
Then I run the application within Tomcat7. I see in the logs:
ServletRegistrationBean: Mapping servlet: 'test' to [/test/]
Then I try to hit the endpoint with cUrl like so:
curl -v http://localhost:8080/test -data-binary '{"test":true}'
or
curl -XPOST -H'Content-type: application/json' http://localhost:8080/test -d '{"test":true}'
I've tried adding a #RequestMapping, but that didn't work either. Can anyone help me figure out how to support another Servlet inside my Spring Boot application?
You can find the sample application here: https://github.com/andrewserff/servlet-demo
Thanks!
From my previous experiences you have to call the servlet with a slash at the end (like http://localhost:8080/test/). If you don't put the slash at the end, the request is routed to the servlet mapped to /, which is by default the DispatcherServlet from Spring (your error message comes from that servlet).
The TestServlet#doPost() implementation calls the super.doPost() - which always sends a 40x error (either 405 or 400 depending on the HTTP Protocol used).
Here's the implementation:
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
A Servlet can be registered using two ways:
registering the Servlet as a Bean (your approach - which should be fine) or
using a ServletRegistrationBean:
#Configuration
public class ServletConfig {
#Bean
public ServletRegistrationBean servletRegistrationBean(){
return new ServletRegistrationBean(new TestServlet(), "/test/*");
}
}
The slightly changed Servlet:
public class TestServlet extends HttpServlet {
private static final Logger log = LoggerFactory.getLogger(TestServlet.class);
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doPost(req, resp);
log.info("doPost was called!!");
}
}
I have a filter in one of the jar files added as reference.
My project is in spring nature. I have developed a web services. All the requests to this web services should be intercepted by my filter "HelloWorld".
This filter is there in one of the reference files.
Here I thought I would implement it as Interceptor.
The filter in reference files looks like
public class HelloWorld implements Filter {
private static final Logger _logger = Logger.getLogger(HelloWorld.class.getName());
protected String name = null;
public HelloWorld()
{
}
public HelloWorld(String name) {
this.name = name;
}
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
PrintWriter out = response.getWriter();
out.println("Hello "+name);
chain.doFilter(req, res);
}
public void init(FilterConfig config) throws ServletException {
//Get init parameter
String testParam = config.getInitParameter("test");
//Print the init parameter
System.out.println("test param: " + testParam);
}
public void destroy() {
//add code to release any resource
}
//some other methods as well
}
What would be the best way to implement this. I cannot configure filter in my web.xml due to limitation in my application.
Can we directly give this HelloWorld filter as reference to interceptor, so that it behaves like interceptor.
Can we change this filter as interceptor in spring and configure in spring.xml without changing the functionality.
Apologies if my question is simple.
Thanks.
I creating a web application using JSF,Hibernate,Spring. I have added a filter for checking session. My Filter code is :
public class AdminFilter implements Filter{
private ArrayList<String> urlList;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
String urls = filterConfig.getInitParameter("avoid-urls");
StringTokenizer token = new StringTokenizer(urls, ",");
urlList = new ArrayList<String>();
while (token.hasMoreTokens()) {
urlList.add(token.nextToken());
}
}
// Checking if user is logged in
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req= (HttpServletRequest) request;
HttpServletResponse resp= (HttpServletResponse) response;
String url = req.getServletPath();
HttpSession session = req.getSession();
if(!urlList.contains(url) && session.getAttribute("user")==null)
{
resp.sendRedirect(req.getContextPath() + "/backend/login/index.xhtml");
}
chain.doFilter(req, resp);
}
#Override
public void destroy() {
// throw new UnsupportedOperationException("Not supported yet.");
}
}
In the init method of filter i have some avoid URL for which session checking should be skipped, like for login page itself. This is working correct but this filter is restricting my CSS,images and JS to load on the login page.
Suggest me what is the problem in my filter ?
Your login page needs some resources (CSS, JS, Images) which are requested from browser in separate request which will be intercepted by Filter and since you don't have any parameters that skips such requests for resources (being used on login page) it will block this request
Suggestion:
You could use Spring-Security, rather than investing time in writing yours, it has got lots of flexibility by configuration
Based on your current config, Currently your filter is ignoring if the URL is for fetching css, images or any other resource.
boolean staticResources = (url.contains("css") || url.contains("images"));
if(!urlList.contains(url) && session.getAttribute("user")==null && !staticResources) {
resp.sendRedirect(req.getContextPath() + "/backend/login/index.xhtml");
}
This will avoid session checking for static contents.
Better way of doing this will be using the declarative security as part of Java EE Web Security using realm.
How can I use a servlet filter to change an incoming servlet request url from
http://nm-java.appspot.com/Check_License/Dir_My_App/Dir_ABC/My_Obj_123
to
http://nm-java.appspot.com/Check_License?Contact_Id=My_Obj_123
?
Update: according to BalusC's steps below, I came up with the following code:
public class UrlRewriteFilter implements Filter {
#Override
public void init(FilterConfig config) throws ServletException {
//
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
String requestURI = request.getRequestURI();
if (requestURI.startsWith("/Check_License/Dir_My_App/")) {
String toReplace = requestURI.substring(requestURI.indexOf("/Dir_My_App"), requestURI.lastIndexOf("/") + 1);
String newURI = requestURI.replace(toReplace, "?Contact_Id=");
req.getRequestDispatcher(newURI).forward(req, res);
} else {
chain.doFilter(req, res);
}
}
#Override
public void destroy() {
//
}
}
The relevant entry in web.xml look like this:
<filter>
<filter-name>urlRewriteFilter</filter-name>
<filter-class>com.example.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>urlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
I tried both server-side and client-side redirect with the expected results. It worked, thanks BalusC!
Implement javax.servlet.Filter.
In doFilter() method, cast the incoming ServletRequest to HttpServletRequest.
Use HttpServletRequest#getRequestURI() to grab the path.
Use straightforward java.lang.String methods like substring(), split(), concat() and so on to extract the part of interest and compose the new path.
Use either ServletRequest#getRequestDispatcher() and then RequestDispatcher#forward() to forward the request/response to the new URL (server-side redirect, not reflected in browser address bar), or cast the incoming ServletResponse to HttpServletResponse and then HttpServletResponse#sendRedirect() to redirect the response to the new URL (client side redirect, reflected in browser address bar).
Register the filter in web.xml on an url-pattern of /* or /Check_License/*, depending on the context path, or if you're on Servlet 3.0 already, use the #WebFilter annotation for that instead.
Don't forget to add a check in the code if the URL needs to be changed and if not, then just call FilterChain#doFilter(), else it will call itself in an infinite loop.
Alternatively you can also just use an existing 3rd party API to do all the work for you, such as Tuckey's UrlRewriteFilter which can be configured the way as you would do with Apache's mod_rewrite.
You could use the ready to use Url Rewrite Filter with a rule like this one:
<rule>
<from>^/Check_License/Dir_My_App/Dir_ABC/My_Obj_([0-9]+)$</from>
<to>/Check_License?Contact_Id=My_Obj_$1</to>
</rule>
Check the Examples for more... examples.
A simple JSF Url Prettyfier filter based in the steps of BalusC's answer. The filter forwards all the requests starting with the /ui path (supposing you've got all your xhtml files stored there) to the same path, but adding the xhtml suffix.
public class UrlPrettyfierFilter implements Filter {
private static final String JSF_VIEW_ROOT_PATH = "/ui";
private static final String JSF_VIEW_SUFFIX = ".xhtml";
#Override
public void destroy() {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = ((HttpServletRequest) request);
String requestURI = httpServletRequest.getRequestURI();
//Only process the paths starting with /ui, so as other requests get unprocessed.
//You can register the filter itself for /ui/* only, too
if (requestURI.startsWith(JSF_VIEW_ROOT_PATH)
&& !requestURI.contains(JSF_VIEW_SUFFIX)) {
request.getRequestDispatcher(requestURI.concat(JSF_VIEW_SUFFIX))
.forward(request,response);
} else {
chain.doFilter(httpServletRequest, response);
}
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
In my case, I use Spring and for some reason forward did not work with me, So I did the following:
public class OldApiVersionFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if (httpServletRequest.getRequestURI().contains("/api/v3/")) {
HttpServletRequest modifiedRequest = new HttpServletRequestWrapper((httpServletRequest)) {
#Override
public String getRequestURI() {
return httpServletRequest.getRequestURI().replaceAll("/api/v3/", "/api/");
}
};
chain.doFilter(modifiedRequest, response);
} else {
chain.doFilter(request, response);
}
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void destroy() {}
}
Make sure you chain the modifiedRequest