Технология сервлетов — подробное объяснение запроса и ответа

Java EE

Request

(I. Обзор

request является параметром метода Servlet.service().Когда клиент делает каждый запрос, сервер создает объект запроса, инкапсулирует данные запроса в запрос, а затем передает их службе при вызове Servlet.service( ) метод. () метод

Объект HttpServletRequest представляет собой запрос клиента. Когда клиент обращается к серверу по протоколу HTTP, вся информация в заголовке HTTP-запроса инкапсулируется в этот объект. Разработчик может получить информацию о клиенте с помощью методов этого объекта.

(2) Общие методы

(1) Метод домена

место хранения

//用来存储一个对象,也可以称之为存储一个域属性
void setAttribute(String name, Object value)
    
Eg:servletContext.setAttribute(“xxx”, “XXX”)
//在ServletContext中保存了一个域属性,域属性名称为xxx,域属性的值为XXX

Получать

//用来获取ServletContext中的数据
Object getAttribute(String name)
//获取名为xx的域属性
Eg:String value = (String)servletContext.getAttribute(“xxx”);

//获取所有域属性的名称;
Enumeration getAttributeNames()

Удалить

//用来移除ServletContext中的域属性
void removeAttribute(String name)

(2) Получить данные заголовка запроса

//获取指定名称的请求头
String getHeader(String name)
 
//获取所有请求头名称
Enumeration getHeaderNames()
    
//获取值为int类型的请求头
int getIntHeader(String name)

(3) Другие методы, связанные с запросом на приобретение

//获取请求体的字节数,GET请求没有请求体,没有请求体返回-1;
int getContentLength()

/*	
	获取请求类型,如果请求是GET,那么这个方法返回null;如果是POST请求,那么默认
	为application/x-www-form-urlencoded,表示请求体内容使用了URL编码;
*/
String getContentType()

//返回请求方法,例如:GET/POST
String getMethod()
  
//返回当前客户端浏览器的Locale。java.util.Locale表示国家和言语,这个东西在国际化中很有用;
Locale getLocale()
    
/*
	获取请求编码,如果没有setCharacterEncoding(),那么返回null,表示使用
	ISO-8859-1编码;
*/
String getCharacterEncoding()
    
/*
	设置请求编码,只对请求体有效!注意,对于GET而言,没有请求体!!!所以此方法
	只能对POST请求中的参数有效!
*/
void setCharacterEncoding(String code)
 
//返回上下文路径,例如:/Dmoe1
String getContextPath()
    
//返回请求URL中的参数,例如:username=zhangSan
String getQueryString()
    
//返回请求URI路径,例如:/Demo1/ServletDemo1
String getRequestURI()
    
/*
	返回请求URL路径,例如:http://localhost/Demo1/ServletDemo1即返回除了参数
	以外的路径信息;
*/
StringBuffer getRequestURL()
    
//返回Servlet路径,例如:/ServletDemo1
String getServletPath()
    
//返回当前客户端的IP地址
String getRemoteAddr()
    
//返回当前客户端的主机名,但这个方法的实现还是获取IP地址
String getRemoteHost()
    
//返回请求协议,例如:http
String getScheme()

//返回主机名,例如:localhost
String getServerName()
    
//返回服务器端口号,例如:8080
int getServerPort()

Чтобы облегчить память, мы рисуем картинку, чтобы помочь памяти

(4) Дело упражнения

Кейс 1: Противоугонная цепочка

Как следует из названия, это означает, что пользователи могут получить доступ только к соответствующим веб-страницам на нашем сайте, и, скопировав ссылку в адресную строку и вставив ссылку на сайты других людей для хотлинкинга, все вернутся на свою собственную страницу ссылок.

Примечание. Некоторые коды ответов не были затронуты. Вы можете сначала нажать на них, чтобы получить общее представление, а затем вернуться к ним позже.

Сначала посмотрите на эффект:

Это веб-сайт, который мы разработали, который просто понимается как официальный веб-сайт.

На официальном сайте нормально переходишь по ссылке, и страница нормально скачет

Если мы пишем страницу локально, обходим a.html, чтобы получить к ней доступhttp://localhost:8080/web-001/ServletDemo3"В это время страница вернется к a.html и вернется на наш официальный веб-сайт, а вывод консоли: незаконный хотлинкинг, возврат к исходной странице для посещения!

Ниже приведена конкретная реализация кода

  • a.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <a href="/web-001/ServletDemo3">葫芦娃最新资源!!!</a>
</body>
</html>
  • ServletDemo3
package cn.ideal.web.request;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/ServletDemo3")
public class RequestDemo1 extends HttpServlet {

    public RequestDemo1() {
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取网页来源
        String referer = req.getHeader("referer");
        //非法盗链
        if (referer == null || !referer.contains("localhost:8080/web-001/a.html")) {
            System.out.println("非法盗链,已经跳回原页面访问!");
            resp.sendRedirect("a.html");
            return;
        }
        //正常访问
        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().write("是他就是他,是他就是他,我们的英雄葫芦娃!!!");
    }
}

(3) Запрос на получение параметров запроса

(1) Где использовать запросы GET/POST

  • Прямой ввод в адресную строку браузера: должен быть запрос GET
  • Гиперссылка: должен быть запрос GET
  • Форма: может быть GET или POST

(2) Разница между запросами GET/POST

A: ПОЛУЧИТЬ запрос
  • Параметры запроса будут отображаться в адресной строке браузера, так что это небезопасно
  • Длина параметра запроса ограничена 1K
  • Запрос GET не имеет тела запроса, и кодировку параметра нельзя установить с помощью request.setCharacterEncoding().
Б: POST-запрос
  • Параметры запроса не будут отображать адресную строку браузера, что относительно безопасно.
  • Нет ограничений на длину параметров запроса

(3) Общий способ получения параметров запроса (как Get/Post)

//根据参数名称获取参数值
String getParameter(String name)

//根据参数名称获取参数值的数组 
String[] getParameterValues(String name)

//获取所有请求的参数名称
Enumeration<String> getParameterNames()

//获取所有参数的map集合
Map<String,String[]> getParameterMap()

(1) Данные для подачи формы [отправить данные по почте]

  • b.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/web-001/RequestDemo3" method="post">
   <table>
      <tr>
         <td>用户名</td>
         <td><input type="text" name="username"></td>
      </tr>
      <tr>
         <td>密码</td>
         <td><input type="password" name="password"></td>
      </tr>
      <tr>
         <td>性别</td>
         <td><input type="radio" name="gender" value="男">男
         <td><input type="radio" name="gender" value="女">女
         </td>
      </tr>
      <tr>
         <td>爱好</td>
         <td>
            <input type="checkbox" name="hobbies" value="游泳">游泳
            <input type="checkbox" name="hobbies" value="跑步">跑步
            <input type="checkbox" name="hobbies" value="网球">网球
      </tr>
      <input type="hidden" name="aaa" value="this is hidden text!">
      <tr>
         <td>从哪来的?</td>
         <td>
            <select name="address">    
               <option value="广州">广州</option>
               <option value="北京">北京</option>
               <option value="深圳">深圳</option>
            </select>
         </td>
      </tr>     
      <tr>
         <td>补充说明</td>
         <td>
            <textarea rows="2" cols="30" name="textarea"></textarea>
         </td>
      </tr>
      <tr>
         <td><input type="submit" value="提交"></td>
         <td><input type="reset" value="重置"></td>
      </tr>
   </table>
   </form>
</body>
</html>
  • RequestDemo3
package cn.ideal.web.request;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;

@WebServlet("/RequestDemo3")
public class RequestDemo3 extends HttpServlet {
    public RequestDemo3() {
        super();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置request字符编码的格式
        req.setCharacterEncoding("UTF-8");

        //通过html的name属性,获取到值
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String gender = req.getParameter("gender");

        //复选框和下拉框有多个值,获取到多个值
        String[] hobbies = req.getParameterValues("hobbies");
        String[] address = req.getParameterValues("address");

        //获取到文本域的值
        String description = req.getParameter("textarea");

        //得到隐藏域的值
        String hiddenValue = req.getParameter("aaa");

        System.out.println("username: " + username);
        System.out.println("password: " + password);
        System.out.println("gender: " + gender);
        System.out.println("hobbies: " + Arrays.toString(hobbies));
        System.out.println("address: " + Arrays.toString(address));
        System.out.println("description: " + description);
        System.out.println("hiddenValue: " + hiddenValue);
    }

}

(2) Отправить данные по гиперссылке [отправить данные по get]

  • c.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<hr/>
<form action="/web-001/RequestDemo4" method="get">
    参数1:<input type="text" name="p1"/><br/>
    参数2:<input type="text" name="p2"/><br/>
    <input type="submit" value="提交"/>
</form>
</body>
</html>
  • RequestDemo4
//省略包
@WebServlet("/RequestDemo4")
public class RequestDemo4 extends HttpServlet {
    public RequestDemo4() {
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String v1 = req.getParameter("p1");
        String v2 = req.getParameter("p2");
        System.out.println("p1=" + v1);
        System.out.println("p2=" + v2);
    }
}

(4) искаженные китайские иероглифы

Проблема искаженных символов в основном связана с предыдущими версиями Tomcat 8. Формат кодирования по умолчанию для Tomcat 8 и выше — UTF-8 вместо ISO 8859-1.

//设置request字符编码的格式
request.setCharacterEncoding("UTF-8");

Кодировка сервера Tomcat по умолчанию — ISO 8859-1, а браузер использует кодировку UTF-8. Китайские данные браузера отправляются на сервер, и Tomacat кодирует китайский язык с помощью кодировки ISO 8859-1.Когда я читаю данные в сервлете, я, естественно, получаю искаженные символы. Поэтому установите кодировку запроса на UTF-8, и искаженные символы будут решены.

Примечание. В приведенном выше примере (с использованием метода post) проблема искаженных символов была решена, но искаженные символы все еще искажаются в методе get. Здесь нам нужно понять, как метод post передает параметры.

Когда мы нажимаем кнопку отправки, данные инкапсулируются в данные формы, а тело объекта передается в HTTP-запрос [передаваемые данные называются основным телом]. Поскольку объект запроса инкапсулирует HTTP-запрос, объект запроса может быть проанализированы для отправки Приходящие данные, поэтому просто установите кодировку на UTF-8, чтобы решить проблему искажения

(Захватите метод почтового запроса в приведенном выше примере)

Отличается метод get, его данные берутся из строки сообщения и не инкапсулируются в запрос, поэтому использование запроса для установки кодировки некорректно.

Решение: Поскольку мы знаем, что кодировка Tomcat по умолчанию — ISO 8859-1, метод get должен быть закодирован в ISO 8859-1, когда тело сообщения передается в браузер.

(Его также можно решить, изменив конфигурацию сервера Tomcat, но делать это не рекомендуется, поскольку он будет слишком сильно зависеть от сервера)

//此时得到的数据已经是被ISO 8859-1编码后的字符串了,这个是乱码
String name = request.getParameter("username);

//乱码通过反向查ISO 8859-1得到原始的数据
byte[] bytes = name.getBytes("ISO 8859-1);

//通过原始的数据,设置正确的码表,构建字符串
String value = new String(bytes,"UTF-8");

(5) Реализовать пересылку

Метод прыжка ресурсов внутри сервера

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/web-001/RequestDemo" method="get">
	<h1>这是转发后的首页,地址栏地址也没有发生变化</h1>		
</form>
</body>
</html>
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		//获取到requestDispatcher对象,跳转到c.html
		RequestDispatcher requestDispatcher = request.getRequestDispatcher("/c.html");
		//调用requestDispatcher对象的forward()实现转发,传入req和resp方法
		requestDispatcher.forward(reqt, resp);

Результат переадресации - адресная строка не изменилась, но страница перескочила на страницу c.html

Изучив ответ, мы изучим проблему перенаправления, а затем отличим ее от переадресации, обратите внимание на эту часть.

(6) Связь между сервлетами

	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		request.setAttribute("username", "admin");
		//获取到requesetDispatcher对象
		RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servletB");
		//调用requestDispatcher对象的forward()实现转发,传入req和resp方法
		requestDispatcher.forward(req, resp);
	}
  • ServletB
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		//获取到存进requeset对象的值
		String username = (String)req.getAttribute("username");
		//在浏览器输出该值
		respe.getWriter().write("i am: " + username);
	}

Мы можем использовать ServletContext и запрашивать одновременно для обеспечения связи между сервлетами.

Вообще говоря, мы стараемся использовать запрос как можно больше, потому что ServletContext представляет собой все веб-приложение.Использование ServetContext будет потреблять много ресурсов, а объект запроса будет обновляться с окончанием запроса, а ресурсы будут перерабатываться. Используйте поле запроса для передачи сервлетов между сервлетами.Связь очень частая в разработке

деталь:

Если часть, написанная в программе сервлета, была фактически передана клиенту до вызова метода foreard, то метод forward вызовет исключение IllegalStateException, то есть не будет записывать данные в браузер перед пересылкой

Если содержимое записывается в буфер движка сервлета до вызова прямого метода, пока содержимое, записанное в буфер, не было фактически выведено клиенту, прямой метод может выполняться нормально, и он был изначально написан в буфер Содержимое будет очищено, но информация поля заголовка ответа, записанная в объект HttpServletResponse, останется действительной

Respone

Объект Request, который мы изучили ранее, может помочь нам получить запрос, отправленный браузером.Чтобы соответствовать, нам нужно изучить объект ответа, который представляет ответ, который может помочь нам ответить клиенту.

(1) Тело ответа

В качестве объекта ответа response предоставляет два объекта потока ответа, которые могут выводить тело ответа клиенту.

//获取字符流
l PrintWriter out = response.getWriter()

//获取字节流
l ServletOutputStream out = response.getOutputStream()
ServletOutputStream servletOutputStream = resp.getOutputStream();
servletOutputStream.write("你好世界".getBytes());
servletOutputStream.write("Just for test".getBytes());

Если версия Tomcat ниже 8 и используется метод print() в outputStream для получения строк, из-за проблем с кодировкой при выводе китайских строк будут искажены символы.

Причина в том, что outputStream выводит двоичные данные.Метод print() сначала имеет процесс преобразования строки в двоичный.Tomcat будет использовать преобразование кодировки IOS 8859-1, так что есть проблема.

Но использование write() может очень хорошо решить эту проблему, потому чтоwrite("Just for test".getBytes());При преобразовании в массив byte[] по умолчанию используется кодировка gb2312, так что проблем не будет

Но для удобства продолжения нам все же придется использовать кодировку UTF-8.Если мы укажем кодировку на предыдущем шаге, посмотрим, как

response.getOutputStream.write("你好世界".getBytes().getBytes("UTF-8"));

В результате получаются искаженные символы, потому что клиентский браузер не знает, в какой кодировке находятся данные ответа, так как же решить эту проблему?

решение:

A: Установить заголовок сообщения

//设置头信息,告诉浏览器我回送的数据是UTF-8的
response.setHeader("Content-Type","text/html;charset=UTF-8");

response.getOutputStream.write("你好世界".getBytes().getBytes("UTF-8"));

B: Используйте теги html для имитации заголовка http-сообщения.

ServletOutputStream servletOutputStream = resp.getOutputStream();

//使用meta标签模拟http消息头,告诉浏览器回送数据的编码和格式
servletOutputStream.write("<meta http-equiv='content-type' content='text/html;charset=UTF-8>".getBytes());

servletOutputStream.write("你好世界".getBytes().getBytes("UTF-8"));

C: Рекомендуемый метод

//设置浏览器用UTF-8编码显示数据
resp.setContentType("text/html;charset=UTF-8");

//获取到printWriter对象
PrintWriter printWriter = resp.getWriter();
printWriter.writer("你好世界")

выгода: не только звонкиresponse.setCharaceterEncoding(“utf-8”), а также устанавливает заголовок ответа типа содержимого (клиентский браузер будет использовать заголовок типа содержимого для интерпретации данных ответа)

Суммировать: Содержимое тела ответа является символьным, затем используйтеrespone.getWriter(), если содержимое ответа — байты, например загрузка файла, вы можете использоватьresponse.getOutputStream()

Уведомление: В одном запросе эти два потока нельзя использовать одновременно, иначе будет выброшено исключение IllegalStateException.

Проблемы с буфером при использовании getWriter()

Его тип — тип PrintWriter, поэтому он имеет буферы, размер буфера по умолчанию — 8 КБ, в рамках определенного подпакета данные сохраняются в буфере, ждите, пока сервер обновит поток, передача данных в буфер Перевернул клиент, если вы хотите ответить на данные, вы можете вызвать метод response.flushbuffer() для ручного обновления буфера.

(2) Установите информацию заголовка ответа, код состояния и другие

(1) Установите заголовок ответа

Используйте метод setHeader() объекта ответа, чтобы установить заголовки ответа.

//设置content-type响应头,告诉浏览器响应内容为html类型,编码为utf-8。而且同时会设置response的字符流编码为utf-8,即response.setCharaceterEncoding(“uaav tf-8”);

response.setHeader(“content-type”, “text/html;charset=utf-8”)

//5秒后自动跳转到指定主页
response.setHeader("Refresh","5; URL=http://www.xxx.com"):

(2) Установите код состояния

//设置状态码
response.setStatus(200)

//当发送错误状态码时,跳转到指定错误页面,但可以显示错误信息
response.sendError(404, “您要查找的资源不存在”)

(3) Другие

//等同于response.setHeader(“content-type”, “text/html;charset=utf-8”)
response.setContentType("text/html;charset=utf-8")

//设置字符响应流的字符编码为UTF-8    
response.setCharacterEncoding(“utf-8”)
    
//下例表示定时刷新,3秒后跳转页面
response.setHeader("Refresh", "3;URL=Bservlet");

(3) Перенаправление

когда вы посещаетеwww.xxx.comКогда страница перенаправляется на другую страницу, а также изменяется URL-адрес в адресной строке браузера, этот метод называется перенаправлением.

Есть два ключевых места для завершения перенаправления

  • установить код ответа
  • Установить заголовок местоположения

Код ответа 200 означает, что ответ был успешным, а код ответа, соответствующий перенаправлению, равен 302, поэтому нам нужно установить код ответа

Поскольку принцип перенаправления заключается в выдаче второго запроса, вам необходимо указать URL-адрес второго запроса для браузера, поэтому заголовок местоположения змейки обязателен.

Примечание. Относительные пути можно использовать на одном сервере.

response.setStatus(302);
response.setHeader("Location", "www.xxx.com");

простое письмо

response.sendRedirect("www.xxx.com");

(4) Различия и сценарии использования между переадресацией и перенаправлением

(1) Разница

(1) Фактическое возникновение неизвестно, а адресная строка отличается

О: Переадресация происходит на сервере

B: Переадресация выполняется сервером, при переадресации адресная строка браузера не меняется, (даже если страница переходит на Servlet2 после доступа к Servlet1, адрес браузера по-прежнему Servlet1) То есть браузер не знает действие перехода, переадресация только один HTTP-запрос, а объекты запроса и ответа в переадресации одинаковы, что также объясняет, почему запрос может использоваться в качестве объекта домена для связи между сервлетами.

C: Перенаправление происходит в браузере

D: Перенаправление осуществляется браузером, адрес браузера будет меняться при перенаправлении и переходе.Принцип перенаправления реализуется путем объединения кода состояния ответа и заголовка местоположения.Запрос http, объект поля запроса недействителен, потому что это не тот же объект запроса

(2) Различное использование

в общем: Для сервера писать сразу от имени ресурса, для браузера нужно вместе писать имя приложения

Request.getRequestDispatcher("/URL-адрес имени ресурса").forward(запрос,ответ);

При пересылке "/" обозначает корневой каталог этого приложения (web-01).

Response.send("/URL-адрес имени веб-приложения/ресурса");

При перенаправлении "/" представляет каталог веб-приложений.

(3) Диапазон URL-адресов, на которые можно перейти, отличается

Форвардинг — это прыжок сервера, который может перейти только к ресурсам текущего веб-приложения.

Редирект — это прыжок сервера, который может перейти на любой ресурс

(4) Различные типы передаваемых данных

Объект переадресованного запроса может передавать различные типы данных, в том числе объекты

Перенаправление может передавать только строки

(5) Время прыжка отличается

При пересылке: оператор Execute to jump перейдет немедленно

Перенаправление: прыжок не будет выполнен, пока не будет выполнена вся страница.

(2) Сценарии применения

Резюме: Переадресация осуществляется с параметрами запроса перед пересылкой. новый запрос на перенаправление

Типичные сценарии применения:

1: Переадресация: доступ к сервлету для обработки бизнес-логики, а затем пересылка его в jsp для обработки результата, URL-адрес в браузере остается неизменным.

2: Перенаправление: отправьте форму, перенаправьте на другой jsp после успешной обработки, чтобы предотвратить повторную отправку формы, и URL-адрес в браузере изменился.

конец:

Если есть какие-либо недостатки или ошибки в содержании, вы можете оставить сообщение для меня, чтобы сделать комментарии, все! ^_^

Если это может вам помочь, то следуйте за мной! (Серия статей будет обновляться по мере возможности на официальном аккаунте)

Мы здесь незнакомцы, но мы все усердно работаем для своей мечты ❤

Публичный аккаунт, настаивающий на продвижении оригинальной технологии Java: в идеале — более двух дней.