This question already has answers here:
doGet and doPost in Servlets
(5 answers)
How do servlets work? Instantiation, sessions, shared variables and multithreading
(8 answers)
Closed 2 years ago.
I am learning how to build Java WebApplications with Servlets.
So far, I set up a Maven project with a Tomcat server and made a basic application with some forms up and running. But I got some basic questions:
What is the difference between the doGet and doPost Methods of the Java Servlet Package? I understand the difference between request and response and I understand HTML GET and POST, but since the application works from the server side, I am confused.
In the example below, why do I need the doPost methods which calls the doGet method (its from a tutorial I use)
When I run the server and open it in the browser, I can submit the form using the button (see below). The button redirets me to /displayuserservlet which displays the first and last name I provided. If I then call /displayuserservlet manually, the first and last names displayed equal to "null". So why is the information not stored on the server (?) and how do I do it (in case that I wan't to store, e.g., a filepath/filename for later use).
My "website"/index.xml `form snippet:
<form action="/displayuserservlet" method="post">
<center>
First name:
<input type="text" name="firstName" value="John">
<br>
Last name:
<input type="text" name="lastName" value="Doe">
<input type="submit"><!-- Press this to submit form -->
</center>
</form>
My Servlet:
#WebServlet("/displayuserservlet")
public class DisplayUserServlet extends HttpServlet {
//REQ is anything that comes FROM the browser
//RES is anything sent TO the browser
#Override
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter pw = res.getWriter();//get the stream to write the data
String title = "Results of form";
String firstName = req.getParameter("firstName");
String lastName = req.getParameter("lastName");
//writing html in the stream
pw.println(ServletUtilities.headWithTitle(title) +
"<body bgcolor=\"#fdf5e6\">\n" +
"<h1>" + title + "</h1>\n" +
"<p>First name:" + firstName + "</p>\n" +
"<p>First name:" + lastName + "</p>\n" +
"</body></html>");
pw.close();//closing the stream
}
#Override
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
doGet(req, res);
}
}
Edited: code format
What is the difference between the doGet and doPost Methods of the Java Servlet Package? I understand the difference between request and response and I understand HTML GET and POST, but since the application works from the server side, I am confused.
In doGet Method the parameters are appended to the URL and sent along with header information. In doPost, parameters are sent in separate line in the body. ... doGet () method generally is used to query or to get some information from the server. doPost () is generally used to update or post some information to the server.
For example, a doGet() URL would look like this:
www.yourwebsite.com/servleturl?parameter1=smething¶meter2=smthingelse
A doPost() URL however, doesn't display the parameters in the URL, so it would just be:
www.yourwebsite.com/servleturl
You should always be using doPost for sensitive information such as a login or a payment form.
In the example below, why do I need the doPost methods which calls the doGet method (its from a tutorial I use)
This is called a POST-REDIRECT-GET. This is basically a feature that prevents the annoying 'Confirm Form Resubmission' message everytime you reload the page, and prevents users from executing an action twice by accident by reloading.
It's pretty hard to explain, but Wikipedia summarizes it very well.
When I run the server and open it in the browser, I can submit the form using the button (see below). The button redirets me to /displayuserservlet which displays the first and last name I provided. If I then call /displayuserservlet manually, the first and last names displayed equal to "null". So why is the information not stored on the server (?) and how do I do it (in case that I wan't to store, e.g., a filepath/filename for later use).
When you manually call /displayuserservlet, it's calling the doGet method. Unless you manually add the parameters in the URL (for example: /displayuserservlet?firstName=John&lastName=Doe which displays John Doe), the request attributes will all be null. Your program works just fine, but it doesn't print anything because you never submitted the parameters.
doGet() and doPost() are called in response to GET and POST requests made by the client. If you want to handle GET and POST requests, but want to handle them in exactly the same way, you can have doGet() call doPost(), or vice versa as in your example. Or you can implement them differently, if you want to handle these request types differently. There are methods for other request types as well -- doPut() for the PUT request, and so on.
The method Request.getParameter() returns the value of a request parameter, that is, a value passed from the client in its request. If the client doesn't supply a value, this method return null. Nothing is stored on the server -- if you want data stored, you get to store it yourself. You can store it transiently, in a Session object, or more permanently in a database or some other storage system.
There are lots of good books on Java web programming. Trying to figure out how all the bits fit together by looking at 'hello, world' examples might not be the best way to get a handle on this very complex subject.
Related
My setup is pretty simple.
I have a Controller with a get- and a postmethod. The getmethod fills a Model which is then used to render a thymeleaftemplate of an HTML-form.
When the User clicks submit the formdata will be posted to the postmethod. This method then performes some businesslogic which may have two outcomes.
First possibilty: Everything works well and a redirect to the getmethod is performed.
Second Possibility: An exception occurs. In that case the user shall be presented with the same form he submitted containing the data he tried to submit. So in that case my postmethod returns the identifying String of the same template as the getmethod. But thymeleaf tells me that the model is lacking some attributes.
Why doesn't spring inject the same model in the postmethod that has been used to render the template of the getmethod.
Is there any way, to recycle the model from the getcall in the postmethod?
I usually redirect after a post method by using the RedirectAttributes. It is used to add data to the model even in a redirect.
This is an example of how to use it:
#PostMapping("/course")
public String addCourse(#RequestParam("course") Course course, RedirectAttributes redirectAttributes) {
// show a popup that the `account.name` has been added
redirectAttrs
.addAttribute("course", course)
.addFlashAttribute("message", "Course created!");
return "redirect:/courseOverview";
}
In my application there are sometimes in that I want to get a user request, save it in some place and then, in a next request, simulate saved request instead of real request, is that possible?
Use any web debugger. I use Fiddler 2.6.2 myself.
Find it here, https://www.telerik.com/download/fiddler
Be warned - there is no robust way to do it (there are a lot of edge cases to consider) but hopefully this will get you started:
Take Request.Form.ToString() and save it against the user somewhere. For testing I suggest putting it in a hidden field, but at some point you would probably need to move that to a database and associate that with the user somehow.
You will need to have a field that will tell you whether the previous request should be replayed. Using a query string parameter like ?replay=true will work. Then pull it into your page:
protected bool IsReplayRequest
{
get
{
return bool.Parse(this.GetValue(p => p.Request["replay"], "false"));
}
}
To simulate the request you will need to override the page's DeterminePostBackMode. This is one of the first methods ASP.NET calls to start the postback process. Use the saved request form converted into a NameValueCollection. Something like this:
protected override NameValueCollection DeterminePostBackMode()
{
if (IsReplayRequest && SavedFormData != null)
{
if (!string.IsNullOrWhiteSpace(SavedFormData.Data))
{
return HttpUtility.ParseQueryString(SavedFormData.Data ?? string.Empty);
}
}
return base.DeterminePostBackMode();
}
Where SavedFormData is just a class which holds the request form and the user information.
With those 3 steps, you should be able to succesfully simulate a previous request for a simple page
Now for the potential problems:
First, the viewstate might get in the way. If you have any listboxes or data grids or authentication etc, you will then need to save the viewstate with the form data. for that you will need to override the page GetStatePersister() method (Is it possible to make a custom implementation of ViewState?).
Second, once you have simulated a request, you need to make sure a) no data is corrupted and b) you remove the "replay" query string. Otherwise the request will play over and over again.
Third, depending on how you store the request form, you will have to think about how to associate it with the user/browser/session/window. Dealing with each one of those has it's own problems.
Once you've got everything above solved, you will successfully simulate a saved request!
Currently I am trying to write a controller in MVC 6 that is capable of accepting data from a post request. The issue is that depending on the client (which is not always a web browser), the data can come in as either key value pairs in the request URL, or as JSON in the request body.
Currently this method works for accepting the data from the URL:
[HttpPost]
public async Task<CaptureResponse> CaptureData(CaptureInfo capture) {
...
}
After a lot of trial and error (and stack overflow answers), I figured out that the [FromBody] attribute tells the model binder to look in the request body, which is required now because MVC 6 combines WebApi and standard MVC together. The following code parses data from JSON in the form body:
[HttpPost]
public async Task<CaptureResponse> CaptureData([FromBody] CaptureInfo capture) {
...
}
For simplicity, I would like to combine the two together somehow, so the model binder gives me the data in the same parameter variable. So far, the only way I can get the data into the same Action is to specify two parameters, one for the URL and one for the body, and do some null checking on each like so:
[HttpPost]
public async Task<CaptureResponse> CaptureData(CaptureInfo capture, [FromBody] CaptureInfo bodyCapture) {
if (bodyCapture != null) {
if (bodyCapture.RequiredProperty1!= null
&& bodyCapture.RequiredProperty2!= null) {
capture = bodyCapture;
}
}
...
}
I have tried specifying multiple properties before the input attribute like this:
[HttpPost]
public async Task<CaptureResponse> CaptureData(CaptureInfo [FromQuery][FromRoute][FromForm][FromBody] capture) {
...
}
But it does not work. Any ideas if something like this is possible?
As far as I know, it is just not possible. Of course you can try using workarounds, basically doing all request parsing yourself. It doesn't sound good, does it?
If you really want the things your way, I believe the best approach is to have two distinct endpoints in the controller and the private method for actual processing. Or, perhaps, even extract that method into an additional abstraction layer with BlaBlaService (CaptureService in your case, probably) class(es) responsible for all the dirty work. Sometimes it makes sense to separate your controllers layer from business logic - for example, for testing purposes.
PS: Your idea is quite similar to what was the approach in good old .NET & PHP times, and believe me, this particular idea is not the one that made those times good. You know, the MVC is much about the REST-like approach, so each endpoint of your controller is supposed to be dedicated to its own single function and obey to a single and uniform "intuitive" protocol.
The "intuitive" way to submit data to POST request for developers acquainted with REST is through the request body. I suggest you to consider going with this approach as the only one.
You need use Request.Form
like:
string username = HttpContext.Current.Request.Form.GetValues("key1")[0].ToString();
string password = HttpContext.Current.Request.Form.GetValues("key2")[0].ToString();
string something = HttpContext.Current.Request.Form.GetValues("key3")[0].ToString();
I am re-writing an ASP.NET application and noticed a difference in behaviour ...
In my Page_Load event I have some code such as:
string id = Request["id"]
which gets the id param from the URL. On page load (ie a HTTP GET), this works as expected in both versions. I also have a button onclick event handler. Clearly, this performs a POST to the server, and also invokes the Page_Load handler. The difference is, that in the original version of the app, the id is successfully loaded from the request. In the new version of the app, id comes back as null. I have discovered that I need to use Request.Params["id"] instead, but am totally puzzled as to why Request["id"] works for POST requests in one app but not the other.
The only difference between the apps is that the first was created as File -> New Website and the second File -> New Web Application. I think this is what is causing the difference in behaviour, but am wondering why this subtle difference, and also if there is anything else I should be aware of between the 2.
Any advice greatly appreciated.
As you have mentioned, you have the id parameter coming through twice. This will be because you have one in the query string parameters and one in the form parameters. I'm not sure why this would be occurring in one web app and not the other, but you can make changes to your code to account for it in a more correct way.
If you view the source of the HTML in your browser, you will see that the action value for the form will be current pages URL, including the query string. This is why the first id is being sent through. Evidently, the second id is coming through via the form itself:
HTML Source of basic web form
<form method="post" action="Default.aspx?id=3" id="ctl01">
<input type="text" name="id">
</div>
There are a couple of things you can do here:
first off, I wouldn't use Request.Params["id"] for this, as it combines the query string, form, cookies and server variables into one collection. You should use Request.Querystring and Request.Form properties, based on what you require and when
In your Page_Load handler, use the Page.IsPostBack property to determine whether the page is loading for a GET or POST and use the Request properties described above.
Example of Page.IsPostBack usage:
protected void Page_Load(object sender, EventArgs e)
{
string id = string.Empty;
if (Page.IsPostBack)
{
id = Request.Form["id"];
}
else
{
id = Request.QueryString["id"];
}
}
I always use web applications project but the difference is compilation. Website has a dynamic compilation, which means that the first request will be slower and web app has pre-compiled release dlls.
Check this for pro's and con's : http://maordavid.blogspot.ca/2007/06/aspnet-20-web-site-vs-web-application.html
I'm rephrasing my existing question to a more generic one. I want to know if Velocity has got implicit object references like JSP does.
I'm particularly interested in knowing about the request object.
In JSP we can get the attribute in the request scope like <%= request.getAttribute("req1") %>
I know that JSP is a servlet and <%= request.getAttribute("req1") %> ends up as a part of _jspService() method which has the request object available to it before the scope of the request ends.
I'm not sure how Velocity works behind the scenes (it may be leaving the request object behind by the time it plays it role)
To test that I did the following thing which was a the part of my previous question.
I have a Spring MVC TestController in which I'm setting a request attribute. I'm using Velocity templates for rendering the views.
#RequestMapping(value="/test", method=RequestMethod.GET)
public ModelAndView display(HttpServletRequest req, HttpServletResponse resp){
...
req.setAttribute("req1", "This should be present for first request");
...
}
In the Velocity template I'm doing something like
Request: $request.getAttribute('req1')
but I'm not getting the value of req1. I know I should have put req1 in model map instead of request but I want to know about implicit request object ref.
I tried $req1 as well but its not working.
When I'm doing the same thing with the model and returning it back, everything is working correctly.
Where am I going wrong?
Update: The same thing is happening with req.getSession().setAttribute("req1", testObject) also.
Salaam,
req.getSession().getAttribute("req1", testObject) == $req1
AFAIK, you cannot access the request object at VelocityViewServlet's templates, unless you explicity set the request object in context or use a v-tool .
Take a look at this question: Velocity + Spring. The Spring folks haven't kept the integration with Velocity very up to date.
Once you've created that extension and set it up to be used properly in your servlet configuration, you'd be able to simply put the object on the ModelAndView and from there do whatever you need with it.