JSP / Servlet
HTTP状态
状态码
代码 | 消息 | 描述 |
---|---|---|
100 | Continue | 只有一个请求的一部分已经由服务器接收,但只要它并没有被拒绝,客户端应继续与该请求 |
101 | Switching Protocols | 服务器切换协议。 |
200 | OK | 请求成功 |
201 | Created | 该请求是完整的,并创建一个新的资源(比如新增加一条记录到数据库操作成功时) |
202 | Accepted | 该请求被接受处理,但是该处理是不完整的。 |
203 | Non-authoritative Information | |
204 | No Content | 资源存在,但内容为空 |
205 | Reset Content | |
206 | Partial Content | |
300 | Multiple Choices | 链接列表。用户可以选择一个链接,进入到该位置。最多五个地址. |
301 | Moved Permanently | 所请求的资源已经转移到一个新的URL |
302 | Found | 请求的资源已经临时切换到一个新的URL(客户端自动跳转) |
303 | See Other | 所请求的资源下,可以找到一个不同的URL(比如,负载均衡) |
304 | Not Modified | 资源没有变化,(客户端可以直接用缓存) |
305 | Use Proxy | |
306 | Unused | 在以前的版本中使用此代码。它不再使用,但被保留的代码。 |
307 | Temporary Redirect | 临时切换到一个新的URL请求的资源。 |
400 | Bad Request | 服务器不理解请求(比如请求的参数不对) |
401 | Unauthorized | 所请求的资源需要一个用户名和密码 |
402 | Payment Required | 您不能使用此代码 |
403 | Forbidden | 被禁止访问所请求的资源 |
404 | Not Found | 服务器无法找到所请求的资源。 |
405 | Method Not Allowed | 在请求中指定的方法是不允许的。 |
406 | Not Acceptable |
服务器不支持指定的表示方式。(如请求头Accept:text/xml ,而服务器程序只返回JSON)
|
407 | Proxy Authentication Required | 您必须使用代理服务器的验证,在此之前请求送达。 |
408 | Request Timeout | 请求需要较长的时间比服务器做好等待的准备。 |
409 | Conflict | 请求无法完成,因为冲突。 |
410 | Gone | 所请求的资源不再可用。 |
411 | Length Required | 没有被定义的「内容长度」。服务器将不接受该请求。 |
412 | Precondition Failed | 给出的先决条件评估为false的服务器的请求。 |
413 | Request Entity Too Large | 该服务器将不接受该请求,因为请求实体过大。 |
414 | Request-url Too Long | 服务器将不接受该请求,因为URL太长。当你转换一个「post」请求一个长的查询信息到一个「get」请求时发生。 |
415 | Unsupported Media Type | 该服务器将不接受该请求,因为不支持的媒体类型。 |
417 | Expectation Failed | |
500 | Internal Server Error | 未完成的请求。服务器遇到了一个意外的情况(通常为程序执行出错) |
501 | Not Implemented | 未完成的请求。服务器不支持所需的功能。 |
502 | Bad Gateway | 未完成的请求。服务器收到无效响应从上游服务器 |
503 | Service Unavailable | 未完成的请求。服务器暂时超载或死机。 |
504 | Gateway Timeout | 网关超时。 |
505 | HTTP Version Not Supported | 服务器不支持「HTTP协议」的版本。 |
HttpServletResponse中指定状态码
setStatus
public void setStatus (int statusCode)
该方法设置一个任意的状态码。setStatus方法接受一个int(状态码)作为参数。
如果您的反应包含了一个特殊的状态码和文档,
请确保在使用PrintWriter
实际返回任何内容之前调用setStatus
。
sendRedirect
public void sendRedirect(String url)
该方法生成一个302
响应,连同一个带有新文档URL的Location头。
sendError
public void sendError(int code, String message)
该方法发送一个状态码(通常为 404), 连同一个在HTML文档内部自动格式化并发送到客户端的短消息。
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import java.util.*; public class showError extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.sendError(404, "Not Funded!!!" ); } }
外部的Servet 容器会把message的内容简单包装为html页面。
异步Servlet
理解异步Servlet之前,让我们试着理解为什么需要它。假设我们有一个Servlet需要很多的 时间来处理,类似下面的内容:
package com.journaldev.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/LongRunningServlet") public class LongRunningServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); System.out.println("LongRunningServlet Start::Name=" + Thread.currentThread().getName() + "::ID=" + Thread.currentThread().getId()); String time = request.getParameter("time"); int secs = Integer.valueOf(time); // max 10 seconds if (secs > 10000) secs = 10000; longProcessing(secs); PrintWriter out = response.getWriter(); long endTime = System.currentTimeMillis(); out.write("Processing done for " + secs + " milliseconds!!"); System.out.println("LongRunningServlet Start::Name=" + Thread.currentThread().getName() + "::ID=" + Thread.currentThread().getId() + "::Time Taken=" + (endTime - startTime) + " ms."); } private void longProcessing(int secs) { // wait for given time before finishing try { Thread.sleep(secs); } catch (InterruptedException e) { e.printStackTrace(); } } }
如果我们的URL是:http://localhost:8080/AsyncServletExample/LongRunningServlet?time=8000
得到响应为"Processing done for 8000 milliseconds! !"。
现在,如果你会查看服务器日志,会得到以下记录:
LongRunningServlet Start::Name=http-bio-8080-exec-34::ID=103 LongRunningServlet Start::Name=http-bio-8080-exec-34::ID=103::Time Taken=8002 ms.
所以Servlet线程实际运行超过8秒,尽管大多数时间用来处理其它Servlet请求或响应。
这可能导致线程饥饿——因为我们的Servlet线程被阻塞,直到所有的处理完成。如果服务器 的请求得到了很多过程,它将达到最大Servlet线程限制和进一步的请求会拒绝连接错误。
Servlet 3.0之前,这些长期运行的线程容器特定的解决方案,我们可以产生一个单独的 工作线程完成耗时的任务,然后返回响应客户。Servlet线程返回Servlet池后启动工作线程 。Tomcat 的 Comet、WebLogic FutureResponseServlet 和 WebSphere Asynchronous Request Dispatcher都是实现异步处理的很好示例。
容器特定解决方案的问题在于,在不改变应用程序代码时不能移动到其他Servlet容器。 这就是为什么在Servlet3.0提供标准的方式异步处理Servlet的同时增加异步Servlet支持。
实现异步Servlet
让我们看看步骤来实现异步Servlet,然后我们将提供异步支持Servlet上面的例子:
- 首先Servlet,我们提供异步支持 Annotation @WebServlet 的属性asyncSupported 值为true。
- 由于实际实现委托给另一个线程,我们应该有一个线程池实现。我们可以一个通过 Executors framework 创建线程池和使用servlet context listener来初始化线程池。
- 通过ServletRequest.startAsync方法获取AsyncContext的实例。AsyncContext提供方法 让ServletRequest和ServletResponse对象引用。它还提供了使用调度方法将请求转发到 另一个 dispatch() 方法。
- 编写一个可运行的实现,我们将进行重处理,然后使用AsyncContext对象发送请求到 另一个资源或使用ServletResponse编写响应对象。一旦处理完成,我们通过 AsyncContext.complete()方法通知容器异步处理完成。
- 添加AsyncListener实现AsyncContext对象实现回调方法,我们可以使用它来提供错误 响应客户端装进箱的错误或超时,而异步线程处理。在这里我们也可以做一些清理工作。
一旦我们将完成我们的项目对于异步Servlet示例,项目结构看起来会像下面的图片:
+ src + com.journaldev.servlet LongRunningServlet.java + com.journaldev.servlet.async AppAsyncListener.java AppcontextListener.java AsyncLongRunningServlet.java AsyncRequestProcesser.java
在监听中初始化线程池
package com.journaldev.servlet.async; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener public class AppContextListener implements ServletContextListener { public void contextInitialized(ServletContextEvent servletContextEvent) { // create the thread pool ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(100)); servletContextEvent.getServletContext().setAttribute("executor", executor); } public void contextDestroyed(ServletContextEvent servletContextEvent) { ThreadPoolExecutor executor = (ThreadPoolExecutor) servletContextEvent .getServletContext().getAttribute("executor"); executor.shutdown(); } }
实现很直接,如果你不熟悉ThreadPoolExecutor 框架请读线程池的ThreadPoolExecutor。
关于listeners 的更多细节,请阅读教程Servlet Listener。
工作线程实现
package com.journaldev.servlet.async; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.AsyncContext; public class AsyncRequestProcessor implements Runnable { private AsyncContext asyncContext; private int secs; public AsyncRequestProcessor() { } public AsyncRequestProcessor(AsyncContext asyncCtx, int secs) { this.asyncContext = asyncCtx; this.secs = secs; } @Override public void run() { System.out.println("Async Supported? " + asyncContext.getRequest().isAsyncSupported()); longProcessing(secs); try { PrintWriter out = asyncContext.getResponse().getWriter(); out.write("Processing done for " + secs + " milliseconds!!"); } catch (IOException e) { e.printStackTrace(); } //complete the processing asyncContext.complete(); } private void longProcessing(int secs) { // wait for given time before finishing try { Thread.sleep(secs); } catch (InterruptedException e) { e.printStackTrace(); } } }
注意:在请求和响应时使用AsyncContext对象,然后在完成时调用
AsyncListener实现
asyncContext.complete()
方法。
package com.journaldev.servlet.async; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebListener; @WebListener public class AppAsyncListener implements AsyncListener { @Override public void onComplete(AsyncEvent asyncEvent) throws IOException { System.out.println("AppAsyncListener onComplete"); // we can do resource cleanup activity here } @Override public void onError(AsyncEvent asyncEvent) throws IOException { System.out.println("AppAsyncListener onError"); //we can return error response to client } @Override public void onStartAsync(AsyncEvent asyncEvent) throws IOException { System.out.println("AppAsyncListener onStartAsync"); //we can log the event here } @Override public void onTimeout(AsyncEvent asyncEvent) throws IOException { System.out.println("AppAsyncListener onTimeout"); //we can send appropriate response to client ServletResponse response = asyncEvent.getAsyncContext().getResponse(); PrintWriter out = response.getWriter(); out.write("TimeOut Error in Processing"); } }
通知的实现在 Timeout()方法,通过它发送超时响应给客户端。
Async Servlet 实现
这是我们的异步Servlet实现,注意使用AsyncContext和ThreadPoolExecutor进行处理。
package com.journaldev.servlet.async; import java.io.IOException; import java.util.concurrent.ThreadPoolExecutor; import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(urlPatterns = "/AsyncLongRunningServlet", asyncSupported = true) public class AsyncLongRunningServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); System.out.println("AsyncLongRunningServlet Start::Name=" + Thread.currentThread().getName() + "::ID=" + Thread.currentThread().getId()); request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true); String time = request.getParameter("time"); int secs = Integer.valueOf(time); // max 10 seconds if (secs > 10000) secs = 10000; AsyncContext asyncCtx = request.startAsync(); asyncCtx.addListener(new AppAsyncListener()); asyncCtx.setTimeout(9000); ThreadPoolExecutor executor = (ThreadPoolExecutor) request .getServletContext().getAttribute("executor"); executor.execute(new AsyncRequestProcessor(asyncCtx, secs)); long endTime = System.currentTimeMillis(); System.out.println("AsyncLongRunningServlet End::Name=" + Thread.currentThread().getName() + "::ID=" + Thread.currentThread().getId() + "::Time Taken=" + (endTime - startTime) + " ms."); } }
Run Async Servlet
现在,当我们将上面运行servlet URL:
http://localhost:8080/AsyncServletExample/AsyncLongRunningServlet?time=8000
得到响应和日志:
AsyncLongRunningServlet Start::Name=http-bio-8080-exec-50::ID=124 AsyncLongRunningServlet End::Name=http-bio-8080-exec-50::ID=124::Time Taken=1 ms. Async Supported? true AppAsyncListener onComplete
如果运行时设置time=9999,在客户端超时以后会得到响应超时错误处理和日志:
AsyncLongRunningServlet Start::Name=http-bio-8080-exec-44::ID=117 AsyncLongRunningServlet End::Name=http-bio-8080-exec-44::ID=117::Time Taken=1 ms. Async Supported? true AppAsyncListener onTimeout AppAsyncListener onError AppAsyncListener onComplete Exception in thread "pool-5-thread-6" java.lang.IllegalStateException: The request associated with the AsyncContext has already completed processing. at org.apache.catalina.core.AsyncContextImpl.check(AsyncContextImpl.java:439) at org.apache.catalina.core.AsyncContextImpl.getResponse(AsyncContextImpl.java:197) at com.journaldev.servlet.async.AsyncRequestProcessor.run(AsyncRequestProcessor.java:27) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918) at java.lang.Thread.run(Thread.java:680)
注意:Servlet线程执行完,很快就和所有主要的处理工作是发生在其他线程。
这是所有异步Servlet内容,希望你喜欢它。code/servlet/AsyncServletExample
MIME操作
格式:
Content-Type: [type]/[subtype]; parameter
其中type
有下面的形式:
- Text
- 用于标准化地表示的文本信息,文本消息可以是多种字符集和或者多种格式的;
- Multipart
- 用于连接消息体的多个部分构成一个消息,这些部分可以是不同类型的数据;
- Application
- 用于传输应用程序数据或者二进制数据;
- Message
- 用于包装一个E-mail消息;
- Image
- 用于传输静态图片数据;
- Audio
- 用于传输音频或者音声数据;
- Video
- 用于传输动态影像数据,可以是与音频编辑在一起的视频数据格式。
subtype
用于指定type
的详细形式。content-type/subtype
配对的集合和与此相关的
参数,将随着时间而增长。为了确保这些值在一个有序而且公开的状态下开发,MIME使用
Internet Assigned Numbers Authority (IANA)作为中心的注册机制来管理这些值。常用的
subtype
值如下所示:
- text/plain(纯文本 )
- text/html(HTML文档)
- application/xhtml+xml(XHTML文档)
- image/gif(GIF图像)
- image/jpeg(JPEG图像)【PHP中为:image/pjpeg】
- image/png(PNG图像)【PHP中为:image/x-png】
- video/mpeg(MPEG动画)
- application/octet-stream(任意的二进制数据)
- application/pdf(PDF文档)
- application/msword(Microsoft Word文件)
- message/rfc822(RFC822 形式)
- multipart/alternative(HTML邮件的HTML形式和纯文本形式,相同内容使用不同形式表示)
- application/x-www-form-urlencoded(使用HTTP的POST方法提交的表单)
- multipart/form-data(同上,但主要用于表单提交时伴随文件上传的场合)
此外,尚未被接受为正式数据类型的subtype,可以使用x-开始的独立名称
(例如:application/x-gzip
)。
vnd-开始的固有名称也可以使用
(例:application/vnd.ms-excel
)。
parameter
可以用来指定附加的信息,更多情况下是用于指定text/plain
和text/htm
等的文字编码方式的charset
参数。 MIME根据type
制定了默认的subtype
,当客户端
不能确定消息的subtype
的情况下,消息被看作默认的subtype
进行处理。Text
默认是
text/plain
,Application
默认是application/octet-stream
,而Multipart
默认
情况下被看作multipart/mixed
。
Content-Disposition简介
Content-Disposition
是HTTP Response Header的一个参数。但是这个不是标准参数,
HTTP/1.1的规范文档中,对于这个参数的解释大意如下:
Content-Disposition
参数本来是为了在客户端另存文件时提供一个建议的文件名,但是
考虑到安全的原因,就从规范中去掉了这个参数。但是由于很多浏览器已经能够支持这个
参数,所以只是在规范文档中列出,但是要注意这个不是HTTP/1.1的标准参数。
其实IE是根据Content-Disposition
中filename
这个段中文件名的后缀来识别
这个文件类型的。
response.addHeader("Content-Disposition", "attachment;filename=test.xls");
那么,在进行Web开发时,可能遇到遇到以下几种需求:
- 希望某类或者某已知MIME 类型的文件(比如:*.gif;.txt;.htm)能够在访问时弹出「文件下载」对话框。
- 希望客户端下载时以指定文件名显示。
- 希望某文件直接在浏览器上显示而不是弹出文件下载对话框。
对于上面的需求,使用Content-Disposition
属性就可以解决。下面是代码示例(Java语言):
response.setHeader("Content-disposition", "attachment;filename=" + fileName);
-
Content-disposition
为属性名。 -
attachment
表示以附件方式下载。如果要在页面中打开,则改为inline
。 -
filename
如果为中文,则会出现乱码。解决办法有两种:
fileName = new String(fileName.getBytes(「GBK」), "ISO-8859-1");
或:
fileName = HttpUtility.UrlEncode(filename, System.Text.Encoding.UTF8);