为啥在使用 HttpWebRequest 时某些链接上出现“(304)未修改”错误?
Posted
技术标签:
【中文标题】为啥在使用 HttpWebRequest 时某些链接上出现“(304)未修改”错误?【英文标题】:Why am I getting "(304) Not Modified" error on some links when using HttpWebRequest?为什么在使用 HttpWebRequest 时某些链接上出现“(304)未修改”错误? 【发布时间】:2011-02-05 21:52:22 【问题描述】:任何想法为什么在我尝试使用 HttpWebRequest 访问的某些链接上我得到“远程服务器返回错误:(304)未修改。”在代码中?
我使用的代码来自Jeff's post here(页面似乎已经消失,请参阅archive copy at the Wayback Machine)。
注意代码的概念是一个简单的代理服务器,所以我将浏览器指向这个本地运行的代码,它获取我的浏览器请求,然后通过创建一个新的 HttpWebRequest 来代理它,就像你一样会在代码中看到。它适用于大多数站点/链接,但对于某些站点/链接来说,会出现此错误。您将在代码中看到一个关键位,它似乎将 http 标头设置从浏览器请求复制到它的请求到站点,并复制到标头属性中。不确定问题是否与它如何模仿请求的这一方面有关,然后当结果返回时会发生什么?
case "If-Modified-Since":
request.IfModifiedSince = DateTime.Parse(listenerContext.Request.Headers[key]);
break;
例如,我从http://en.wikipedia.org/wiki/Main_Page 得到问题
PS。在这里更新
仍然无法解决这个问题。基本上我可以确定 1 个有问题的链接,它似乎工作正常,第二次出现错误,第三次正常,第四次出现错误,第五次正常等。好像有一些状态没有被清除或代码中的一些东西。我尝试使用“使用”类型语句等来清理代码。
这是代码。如果有人能发现为什么我每次第二次通过此代理代码浏览到 http://newsimg.bbc.co.uk/css/screen/1_0_16/nol/v4/story.css 之类的链接(从第二次开始,而不是第一次),我都会收到我很想听到的错误。
class Program
static void Main(string[] args)
Proxy p = new Proxy(8080);
Thread proxythread = new Thread(new ThreadStart(p.Start));
proxythread.Start();
Console.WriteLine("Proxy Started. Press Any Key To Stop...");
Console.ReadKey();
p.Stop();
public class Proxy
private HttpListener _listener;
private int _port;
public Proxy(int port)
int defaultport = 8080;
// Setup Thread Pool
System.Threading.ThreadPool.SetMaxThreads(50, 1000);
System.Threading.ThreadPool.SetMinThreads(50, 50);
// Sanitize Port Number
if (port < 1024 || port > 65535)
port = defaultport;
// Create HttpListener Prefix
string prefix = string.Format("http://*:0/", port);
_port = port;
// Create HttpListener
_listener = new HttpListener();
_listener.Prefixes.Add(prefix);
public void Start()
_listener.Start();
while (true)
HttpListenerContext request = null;
try
request = _listener.GetContext();
// Statistics (by Greg)
int availThreads = -1;
int compPortThreads = -1;
ThreadPool.GetAvailableThreads(out availThreads, out compPortThreads);
log("INFO", request.Request.Url.ToString(), "START - [" + availThreads + "]");
ThreadPool.QueueUserWorkItem(ProcessRequest, request);
catch (HttpListenerException ex)
log("ERROR", "NA", "INFO: HttpListenerException - " + ex.Message);
break;
catch (InvalidOperationException ex)
log("ERROR", "NA", "INFO: InvalidOperationException - " + ex.Message);
break;
public void Stop()
_listener.Stop();
private void log(string sev, string uri, string message)
Console.Out.WriteLine(Process.GetCurrentProcess().Id + " - " + sev + " (" + uri + "): " + message);
private void ProcessRequest(object _listenerContext)
#region local variables
HttpWebRequest psRequest; // Request to send to remote web server
HttpWebResponse psResponse; // Response from remote web server
List<byte> requestBody = new List<byte>(); // Byte array to hold the request's body
List<byte> responseBody = new List<byte>(); // Byte array to hold the response's body
byte[] buffer;
string uri = "";
#endregion
var listenerContext = (HttpListenerContext)_listenerContext;
uri = listenerContext.Request.Url.ToString().Replace(string.Format(":0", _port), "");
// Create Interent Request
HttpWebRequest internetRequest = (HttpWebRequest)WebRequest.Create(uri);
#region Build Request Up
internetRequest.Method = listenerContext.Request.HttpMethod;
internetRequest.ProtocolVersion = listenerContext.Request.ProtocolVersion;
internetRequest.UserAgent = listenerContext.Request.UserAgent;
foreach (string key in listenerContext.Request.Headers.AllKeys)
try
switch (key)
case "Proxy-Connection":
case "Connection":
internetRequest.KeepAlive = (listenerContext.Request.Headers[key].ToLower() == "keep-alive") ? true : false;
break;
case "Content-Length":
internetRequest.ContentLength = listenerContext.Request.ContentLength64;
break;
case "Content-Type":
internetRequest.ContentType = listenerContext.Request.ContentType;
break;
case "Accept":
internetRequest.Accept = listenerContext.Request.Headers[key];
break;
case "Host":
break;
case "Referer":
internetRequest.Referer = listenerContext.Request.Headers[key];
break;
case "If-Modified-Since":
internetRequest.IfModifiedSince = DateTime.Parse(listenerContext.Request.Headers[key]);
break;
default:
internetRequest.Headers.Add(key, listenerContext.Request.Headers[key]);
break;
catch (Exception ex)
Console.WriteLine("Error settup up psRequest object. Error = " + ex.Message + "\n" + ex.StackTrace);
#endregion
#region Copy content into request
buffer = new byte[1024];
using (Stream instream = listenerContext.Request.InputStream)
int incount = instream.Read(buffer, 0, buffer.Length);
while (incount > 0)
internetRequest.GetRequestStream().Write(buffer, 0, incount);
incount = instream.Read(buffer, 0, buffer.Length);
#endregion
// Get Internet Response
HttpWebResponse internetResponse = null;
try
using (internetResponse = (HttpWebResponse)internetRequest.GetResponse())
#region Configure Local Response Header Keys
foreach (string key in internetResponse.Headers.Keys)
try
switch (key)
case "Transfer-Encoding":
listenerContext.Response.SendChunked = (internetResponse.Headers[key].ToLower() == "chunked") ? true : false;
break;
case "Content-Length":
listenerContext.Response.ContentLength64 = internetResponse.ContentLength;
break;
case "Content-Type":
listenerContext.Response.ContentType = internetResponse.Headers[key];
break;
case "Keep-Alive":
listenerContext.Response.KeepAlive = true;
break;
default:
listenerContext.Response.Headers.Add(key, internetResponse.Headers[key]);
break;
catch (Exception ex)
log("ERROR", uri, "Error settup up listenerContext.Response objects. Error = " + ex.Message + "\n" + ex.StackTrace);
#endregion
try
// Transfer the body data from Internet Response to Internal Response
buffer = new byte[1024];
using (Stream inputStream = internetResponse.GetResponseStream())
int outcount = inputStream.Read(buffer, 0, buffer.Length);
while (outcount > 0)
listenerContext.Response.OutputStream.Write(buffer, 0, outcount);
outcount = inputStream.Read(buffer, 0, buffer.Length);
catch (Exception ex)
log("ERROR", uri, "Could not obtain response from URI: " + ex.Message);
finally
listenerContext.Response.OutputStream.Close();
catch (Exception ex)
//if (ex is InvalidOperationException ||
// ex is ProtocolViolationException ||
// ex is WebException)
//
// log(uri, "Could not successfully get response: " + ex.GetType() + " - " + ex.Message);
// listenerContext.Response.Close();
// return;
//
//else throw;
log("ERROR", uri, "Could not successfully get response: " + ex.GetType() + " - " + ex.Message);
listenerContext.Response.Close();
这是我看到的一个例子 - 第一次命中很好,第二次有错误......
Proxy Started. Press Any Key To Stop...
2080 - INFO (http://newsimg.bbc.co.uk:8080/css/screen/1_0_16/nol/v4/story.css): START - [50]
2080 - INFO (http://newsimg.bbc.co.uk:8080/css/screen/1_0_16/nol/v4/story.css): START - [50]
2080 - ERROR (http://newsimg.bbc.co.uk/css/screen/1_0_16/nol/v4/story.css): Could not successfully get response: System.Net.WebException - The remote server returned an error: (304) Not Modified.
【问题讨论】:
HttpWebRequest.GetResponse throws WebException on HTTP 304的可能重复 304 不是错误! 【参考方案1】:首先,这不是错误。 3xx
表示重定向。真正的错误是4xx
(客户端错误)和5xx
(服务器错误)。
如果客户端获得304 Not Modified
,则客户端有责任从自己的缓存中显示有问题的资源。一般来说,代理不应该担心这个。它只是信使。
【讨论】:
谢谢 - 那么你是说,我应该做的是仍然捕获异常(就在我正在执行请求的时候),但是然后检查异常中的内容以及是否是 304 然后继续吗? 否 - 如果您发送了 if-modified-since 或 if-none-match,则意味着您有一个本地(缓存)副本 - 服务器指示您使用该副本。跨度> 好的——只需要确保我在本地请求中返回了相同的状态码,这似乎有效。有关代码的更多详细信息,请参阅***.com/questions/2611264/… 这意味着例如来自 API 的数据与客户端中可用的缓存数据相比没有变化,因此导致 304 状态码对吗?【参考方案2】:这是预期行为。
当您发出 HTTP 请求时,服务器通常会返回代码 200 OK
。如果你设置If-Modified-Since
,服务器可能会返回304 Not modified
(并且响应不会有内容)。这应该是您的提示,该页面尚未被修改。
304
的authors of the class have foolishly decided 应该被视为错误并抛出异常。现在,您必须在每次尝试使用 If-Modified-Since
时捕获异常来清理它们。
【讨论】:
这绝对是疯了。实际的错误代码是否应该引起异常是有争议的。这个异常是理解异常行为的可怕失败。【参考方案3】:仅按 F5 并不总是有效。
为什么?
因为您的 ISP 也在为您缓存网络数据。
解决方案:强制刷新。
通过在 Firefox 或 Chrome 中按 CTRL + F5 强制刷新浏览器以清除 ISP 缓存,而不是仅按 F5
然后您可以在浏览器 F12 开发者工具网络选项卡中看到 200 响应而不是 304。
另一个技巧是在请求页面的URL字符串末尾添加问号?
:
http://localhost:52199/Customers/Create?
问号将确保浏览器刷新请求而不缓存任何以前的请求。
另外,在 Visual Studio 中,您可以在 Incognito 模式下将默认浏览器设置为 Chrome 以避免在开发时出现缓存问题,方法是在隐身模式作为默认浏览器,请参见步骤(自图解):
【讨论】:
【参考方案4】:这不是缓存问题...
要克服这个问题,请在您的端点调用中添加一个时间戳,例如axios.get('/api/products')
.
时间戳之后应该是axios.get(/api/products?$Date.now()
。
它将解析您的 304 状态代码。
【讨论】:
【参考方案5】:我认为您还没有安装这些功能。见下图。
几天前我也遇到过这个问题。安装此功能后,我解决了它。如果您还没有安装此功能,请安装它。
安装过程:
-
去安卓工作室
工具
安卓
SDK 管理器
外观和行为
android SDK
【讨论】:
不..这不是答案以上是关于为啥在使用 HttpWebRequest 时某些链接上出现“(304)未修改”错误?的主要内容,如果未能解决你的问题,请参考以下文章
如何在向某些站点发出 HttpWebRequest 时修复“底层连接已关闭:连接已意外关闭”
为啥使用 HttpClient 而不是 HttpWebRequest 进行同步请求
为啥 HttpWebRequest 会抛出异常而不是返回 HttpStatusCode.NotFound?