Read index.html directly without exposing its path in URL - java

I have an index.html file in the following structure.
WEB-INF/dbName/type/id/index.html
Also, there are external css and js files in the same path.
I need to access this file without exposing its path in the browser.
Below is my controller class:
#Controller
public class MyController {
private static final Logger logger = LoggerFactory.getLogger(MyController.class);
#RequestMapping(value = "/{type}/{id}", method = RequestMethod.GET, produces = MediaType.ALL_VALUE)
public String pod(HttpServletRequest request, #PathVariable String type, #PathVariable String id)
throws IOException {
String redirectUrl = "/dbName/" + type + "/" + id + "/index.html";
return "redirect:" + redirectUrl;
}
}
Resource mapping in servlet-dispatcher.xml:
<mvc:resources mapping="/dbName/**" location="/WEB-INF/dbName/" />
The resulting URL looks like this: localhost:8080/project/dbName/type/id/index.html
But, the expected URL is: localhost:8080/project/type/id/
This code exposes the URL to the resource in the browser. Then, I tried using forward instead of redirect. Then it doesn't expose the URL, but the external CSS and js used in index.html are not loaded.
Any help would be appreciated.

The contract of a view resolver specifies that a view resolver can return null to indicate the view could not be found. Not all view resolvers do this, however, because in some cases, the resolver simply cannot detect whether or not the view exists. For example, the InternalResourceViewResolver uses the RequestDispatcher internally, and dispatching is the only way to figure out if a JSP exists, but this action can only execute once. The same holds for the VelocityViewResolver and some others. Check the javadocs of the specific view resolver to see whether it reports non-existing views. Thus, putting an InternalResourceViewResolver in the chain in a place other than the last results in the chain not being fully inspected, because the InternalResourceViewResolver will always return a view!
Redirecting to Views
As mentioned previously, a controller typically returns a logical view name, which a view resolver resolves to a particular view technology. For view technologies such as JSPs that are processed through the Servlet or JSP engine, this resolution is usually handled through the combination of InternalResourceViewResolver and InternalResourceView, which issues an internal forward or include via the Servlet API’s RequestDispatcher.forward(..) method or RequestDispatcher.include() method. For other view technologies, such as Velocity, XSLT, and so on, the view itself writes the content directly to the response stream.
RedirectView
One way to force a redirect as the result of a controller response is for the controller to create and return an instance of Spring’s RedirectView. In this case, DispatcherServlet does not use the normal view resolution mechanism. Rather because it has been given the (redirect) view already, the DispatcherServlet simply instructs the view to do its work. The RedirectView in turn calls HttpServletResponse.sendRedirect() to send an HTTP redirect to the client browser.
If you use RedirectView and the view is created by the controller itself, it is recommended that you configure the redirect URL to be injected into the controller so that it is not baked into the controller but configured in the context along with the view names. The the section called “The redirect: prefix” facilitates this decoupling.
The redirect: prefix
While the use of RedirectView works fine, if the controller itself creates the RedirectView, there is no avoiding the fact that the controller is aware that a redirection is happening. This is really suboptimal and couples things too tightly. The controller should not really care about how the response gets handled. In general it should operate only in terms of view names that have been injected into it.
The special redirect: prefix allows you to accomplish this. If a view name is returned that has the prefix redirect:, the UrlBasedViewResolver (and all subclasses) will recognize this as a special indication that a redirect is needed. The rest of the view name will be treated as the redirect URL.
The net effect is the same as if the controller had returned a RedirectView, but now the controller itself can simply operate in terms of logical view names. A logical view name such as redirect:/myapp/some/resource will redirect relative to the current Servlet context, while a name such as redirect:http://myhost.com/some/arbitrary/path will redirect to an absolute URL.
The forward: prefix
It is also possible to use a special forward: prefix for view names that are ultimately resolved by UrlBasedViewResolver and subclasses. This creates an InternalResourceView (which ultimately does a RequestDispatcher.forward()) around the rest of the view name, which is considered a URL. Therefore, this prefix is not useful with InternalResourceViewResolver and InternalResourceView (for JSPs for example). But the prefix can be helpful when you are primarily using another view technology, but still want to force a forward of a resource to be handled by the Servlet/JSP engine. (Note that you may also chain multiple view resolvers, instead.)
As with the redirect: prefix, if the view name with the forward: prefix is injected into the controller, the controller does not detect that anything special is happening in terms of handling the response.
DOCS: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html

The RequestDispatcher class and it's forward method like you've mentioned should work.
RequestDispatcher rd = req.getRequestDispatcher("index.html");
rd.forward(req, resp);
This should serve the index.html page nodifferently than visiting it directly.
It's confusing why the CSS and JS would fail to work using this method. I don't believe there is anything different between requesting /index.html or using a servlet to request index.html that would cause this. Maybe check that these files are correctly referenced in your HTML?
I know this would be better suited to a comment but I haven't yet earned enough reputation, sorry.

The redirect sends the URL back to browser first and then from browser another request is created, redirect can redirect to resources on different servers. Since the url with redirect action is sent back to browser, causes the browser to show the URL in client's address bar
Just use
request.getRequestDispathcer("url").forward(//required params);
Forward request is used to forward to resources available within the server from where the call is made. This transfer of control is done by the container internally and browser / client is not involved.

Related

How to make a servlet handle all URLs except JSPs [duplicate]

The familiar code:
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
My understanding is that /* maps to http://host:port/context/*.
How about /? It sure doesn't map to http://host:port/context root only. In fact, it will accept http://host:port/context/hello, but reject http://host:port/context/hello.jsp.
Can anyone explain how is http://host:port/context/hello mapped?
<url-pattern>/*</url-pattern>
The /* on a servlet overrides all other servlets, including all servlets provided by the servletcontainer such as the default servlet and the JSP servlet. Whatever request you fire, it will end up in that servlet. This is thus a bad URL pattern for servlets. Usually, you'd like to use /* on a Filter only. It is able to let the request continue to any of the servlets listening on a more specific URL pattern by calling FilterChain#doFilter().
<url-pattern>/</url-pattern>
The / doesn't override any other servlet. It only replaces the servletcontainer's built in default servlet for all requests which doesn't match any other registered servlet. This is normally only invoked on static resources (CSS/JS/image/etc) and directory listings. The servletcontainer's built in default servlet is also capable of dealing with HTTP cache requests, media (audio/video) streaming and file download resumes. Usually, you don't want to override the default servlet as you would otherwise have to take care of all its tasks, which is not exactly trivial (JSF utility library OmniFaces has an open source example). This is thus also a bad URL pattern for servlets. As to why JSP pages doesn't hit this servlet, it's because the servletcontainer's built in JSP servlet will be invoked, which is already by default mapped on the more specific URL pattern *.jsp.
<url-pattern></url-pattern>
Then there's also the empty string URL pattern . This will be invoked when the context root is requested. This is different from the <welcome-file> approach that it isn't invoked when any subfolder is requested. This is most likely the URL pattern you're actually looking for in case you want a "home page servlet". I only have to admit that I'd intuitively expect the empty string URL pattern and the slash URL pattern / be defined exactly the other way round, so I can understand that a lot of starters got confused on this. But it is what it is.
Front Controller
In case you actually intend to have a front controller servlet, then you'd best map it on a more specific URL pattern like *.html, *.do, /pages/*, /app/*, etc. You can hide away the front controller URL pattern and cover static resources on a common URL pattern like /resources/*, /static/*, etc with help of a servlet filter. See also How to prevent static resources from being handled by front controller servlet which is mapped on /*. Noted should be that Spring MVC has a built in static resource servlet, so that's why you could map its front controller on / if you configure a common URL pattern for static resources in Spring. See also How to handle static content in Spring MVC?
I'd like to supplement BalusC's answer with the mapping rules and an example.
Mapping rules from Servlet 2.5 specification:
Map exact URL
Map wildcard paths
Map extensions
Map to the default servlet
In our example, there're three servlets. / is the default servlet installed by us. Tomcat installs two servlets to serve jsp and jspx. So to map http://host:port/context/hello
No exact URL servlets installed, next.
No wildcard paths servlets installed, next.
Doesn't match any extensions, next.
Map to the default servlet, return.
To map http://host:port/context/hello.jsp
No exact URL servlets installed, next.
No wildcard paths servlets installed, next.
Found extension servlet, return.
Perhaps you need to know how urls are mapped too, since I suffered 404 for hours. There are two kinds of handlers handling requests. BeanNameUrlHandlerMapping and SimpleUrlHandlerMapping. When we defined a servlet-mapping, we are using SimpleUrlHandlerMapping. One thing we need to know is these two handlers share a common property called alwaysUseFullPath which defaults to false.
false here means Spring will not use the full path to mapp a url to a controller. What does it mean? It means when you define a servlet-mapping:
<servlet-mapping>
<servlet-name>viewServlet</servlet-name>
<url-pattern>/perfix/*</url-pattern>
</servlet-mapping>
the handler will actually use the * part to find the controller. For example, the following controller will face a 404 error when you request it using /perfix/api/feature/doSomething
#Controller()
#RequestMapping("/perfix/api/feature")
public class MyController {
#RequestMapping(value = "/doSomething", method = RequestMethod.GET)
#ResponseBody
public String doSomething(HttpServletRequest request) {
....
}
}
It is a perfect match, right? But why 404. As mentioned before, default value of alwaysUseFullPath is false, which means in your request, only /api/feature/doSomething is used to find a corresponding Controller, but there is no Controller cares about that path. You need to either change your url to /perfix/perfix/api/feature/doSomething or remove perfix from MyController base #RequestingMapping.
I think Candy's answer is mostly correct. There is one small part I think otherwise.
To map host:port/context/hello.jsp
No exact URL servlets installed, next.
Found wildcard paths servlets, return.
I believe that why "/*" does not match host:port/context/hello because it treats "/hello" as a path instead of a file (since it does not have an extension).
The essential difference between /* and / is that a servlet with mapping /* will be selected before any servlet with an extension mapping (like *.html), while a servlet with mapping / will be selected only after extension mappings are considered (and will be used for any request which doesn't match anything else---it is the "default servlet").
In particular, a /* mapping will always be selected before a / mapping. Having either prevents any requests from reaching the container's own default servlet.
Either will be selected only after servlet mappings which are exact matches (like /foo/bar) and those which are path mappings longer than /* (like /foo/*). Note that the empty string mapping is an exact match for the context root (http://host:port/context/).
See Chapter 12 of the Java Servlet Specification, available in version 3.1 at http://download.oracle.com/otndocs/jcp/servlet-3_1-fr-eval-spec/index.html.

Is it the only way of URL mapping to select a controller to run?

The first step, in SpringMVC application, may be mapping a URL to one Controller (not a Jsp page) to display the HomePage using GET method, isn't it? After the form, in my spicific app, is filled, the same controller get the information, do some verifications and the return a logical view name to InternalViewResolver to show the second page.
My question is: when user click a icon or button in the second page, how does the next controller be selected to run? Is still a URL mapping? or any way else?
thanks!
kenneth
Short answer is, Yes. Whether you use ajax request or form submission, you will need to have a controller with appropriate mapping url that will process your request further depending on the code your controller contains.
when user click a icon or button in the second page, how does the next controller be selected to run? Is still a URL mapping? or any way else?
yes, because Spring MVC uses url pattern which itself is a concise and simple way for mapping set of urls.
When you click image or link in ur view page, you allow Controller and DispatcherServlet to receive user's request and after that dispatch request to controller. Then MappingHandler will choose which one is right url request that client requesting by following HttpServletRequest mechanism.
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
After this Controller will do real work of processing request/view page.
Note that: URL pattern itself follow simple ways to run the correct url by analyzing every character in a pattern which must match the corresponding character in the URL path exactly with two exceptions.
In Spring MVC, you can do either this
Other site
or that
<spring:url value="/othersite"/>
then URL pattern will be matched by controller itself.

JSTL objects in the url, not in tag context

So I'm writing a Spring 3 webapp with JSP views and JSTL tags. They normally work great, but there's this one controller call that doesn't grab the tags properly.
ModelAndView mav = new ModelAndView(
new RedirectView(RequestUtil.getWebAppRoot(request) + clientShortName, false)
);
mav.addObject("status","Session for interface successfully removed");
return mav;
So when I go to reference it in my view, I'll have a line that looks like:
<p>status="${status}"</p>
Which just displays as:
status=""
Now I would normally just dismiss this as something causing my view to render improperly, but I actually found this sitting appended to my URL:
?status=Session+for+interface+zFXDEV3+successfully+removed
So this leaves me with two questions:
Why can't I reference the object from a JSTL tag?
If I can't get it as a part of the tag context, what is it doing in the URL?
and for anyone wondering, the class types are:
org.springframework.web.servlet.view.RedirectView.RedirectView
org.springframework.web.servlet.ModelAndView.ModelAndView(View view)
This is not JSTL but Expression Language (commonly known as EL). The problem is that EL ${status} will look the variable in the request attributes, but when you redirect to your JSP you have status as request parameter but not as request attribute (note that this is normal behavior when you redirect to a page).
For a better example (taken from StackOverflow Expression Language code), this is what is executed:
<%
String status = (String) pageContext.findAttribute("status");
if (status != null) {
out.print(status);
}
%>
You have two possible options here:
As stated by #SotiriosDelimanolis, your #Controller class for this URL should take the request parameters and add them as request attributes. Lot of work if you could add more request parameters in the future.
Use the ${param} object from EL that gives you access to the request parameters. Using this, you should change ${status} to ${param.status}. End of story.
Because it is a RedirectView. The javadoc says:
By default all primitive model attributes (or collections thereof) are
exposed as HTTP query parameters (assuming they've not been used as
URI template variables), but this behavior can be changed by
overriding the isEligibleProperty(String, Object) method.
So your String objects are added as query parameters in the new, redirected, request. They are no longer available as model/request attributes to the new request.
The #Controller that handles the redirected URL should re-add the attribute to the model.

does mvc architecture affect SEO?

I have my MVC architecture in my webapplication.Now the flow is as follows:
starter.jsp -> SERVLET ->view.jsp
Now view.jsp expects cetrtain request parameters to be present in request object.
When view.jsp is called using above workflow everything works fine.But if view.jsp is called directly then it does not get expected attributes in request and forwards to error.jsp
Now Will search engine directly call view.jsp? or will it travel from starter.jsp to view.jsp?
Detailed example:
main page has a href to a article.
href ="servlet?id=xyz"
Now servlet gets the id from request.
Servlet gets details for id from DB , puts the Object obtained from DB as request attribute
idDetails
And forwards to view.jsp
View.jsp gets request attribute idDetails
So view.jsp expects idDetails to be present as request attribute
There is no correlation between SEO and MVC. MVC manages the internals of your application while SEO is some external tweak. If they correlate somehow then there is something wrong with your application in my opinion.
If some files are public so the search engine can see them it is likely that they will be indexed. If in doubt you can always check your pages in google for example using the site: prefix.
So the point is that if you can see it google can see it.
I agree with Adam's reply. SEO and MVC architecture are not related at all.
If you have fear like, what happen If "CRAWLERS" will directly access your view.jsp page, then I suggest you to use Filters and Interceptors in your application.
So here your interceptor/filter will intercept the incoming request and filter it out if they are directly made by "Crawler Algorithm" OR "Suspect User".

FacesServlet and URL Mapping in Web.xml

HI,
We are declaring the FacesServlet and its URL mapping in the Web.xml. From my understanding,
FacesServlet loaded only once at the server startup.
URL mapping is used only when first time JSP application accessed from the external context.
One of the new learner for JSF has asked me the questions, these two things are used only once by the application. Is it true? Also is there any other way by not including in the web.xml?
What I should answer?
Updated
For example, I am accessing the application using the URL http://localhost:8080/webapp/index.jsf. When we are accessing this URL, FacesServlet invoked and view is rendered. The following is my question:
In JSF, we never seen changing the URL in the address bar. In that case, how it is handling the new request with the same URL?
In faces-config.xml we are giving the navigation cases as follows:
to-view-id>failure.jsp /to-view-id>
Why we need not give the view name as failure.jsf? We are just giving the *.jsp in the faces-config.xml. How it is handled internally?
FacesServlet loaded only once at the server startup.
Correct.
URL mapping is used only when first time JSP application accessed from the external context.
Incorrect. It's been tested on every incoming HttpServletRequest. How else should the container know which servlet to invoke?
Also is there any other way by not including in the web.xml?
If you're using a servletcontainer which supports Servlet 3.0, you can also do this by #WebServlet annotation. JSF 2.0, however, is designed to be backwards compatible with Servlet 2.5, so it doesn't ship with that annotation and you need to explicitly declare it in web.xml.
See also:
Servlets tag info page
Lifecycle of a JSP/Servlet webapplication
How servlets are initialized and used
Update as per the new series of questions (which should each belong in its own question, but ala)
In JSF, we never seen changing the URL in the address bar. In that case, how it is handling the new request with the same URL?
This happens only if under the covers a forward by RequestDispatcher#forward() takes place. In a forward, the servletcontainer basically reuses the same HTTP request/response for a view (JSP/XHTML page). It does not force/instruct the webbrowser to send a brand new request. On the other hand, the HttpServletResponse#sendRedirect() will instruct the client (the webbrowser) to fire a new GET request and thus the URL will change. You can force this in the JSF sice by adding <redirect/> to the <navigation-case>. Note that since this causes a new request, all request scoped beans of the initial request will be lost.
Why we need not give the view name as failure.jsf? We are just giving the *.jsp in the faces-config.xml. How it is handled internally?
The FacesServlet knows its own url-pattern.
Yes. while loading your application container will load web.xml and will extract the data for
particular URL pattern to servlet . when request comes it checks from memory that for this pattern which servlet to invoke and then if servlet is already loaded it will take it from memory otherwise it will create an instance of servlet and it will invoke doGet() or doPost() depending on the request type.
and there is another way to delcare URL mapping as from JAVAEE-6 by annotation
something like
import javax.servlet.annotation.InitParam;
import javax.servlet.annotation.WebServlet;
#WebServlet(
name = "SimpleServlet",
urlPatterns = {"/login"},
initParams = {
#InitParam(name = "param1", value = "value1"),
#InitParam(name = "param2", value = "value2")}
)
public class SimpleServlet {
}
In faces-config.xml we are giving the navigation cases as follows:
<to-view-id>failure.jsp </to-view-id>
Why we need not give the view name as failure.jsf? We are just giving the *.jsp in the faces-config.xml. How it is handled internally?
it is view identifier not the URL FacesServlet will load that view upon invocation of that navigation case.

Categories