请帮我解决 W3C 验证 API 超时问题
我正在使用 W3C 验证服务来检查我在 TextBox 中输入的文本是否是有效的标记。
快要工作了。 但是,在特定条件下,我的输入会导致错误,然后导致无休止的超时异常。 我必须关闭并重新打开程序才能使其再次运行。
请看一下我的代码并帮助我解决这个问题。
我有一个非常简单的 WPF 应用程序,带有一个文本框和一个状态栏。 当我键入时,状态栏会更新,让我知道我键入的标记是否有效。 因此,我不会破坏该服务,验证仅在经过一秒或更长时间并且没有击键的情况下才会发生。
无效的 http://img9.imageshack.us/img9/3788/invalidr.gif
StatusBar 可能会显示:“正在验证...”、“有效”、“无效”,或者——如果有的话——异常消息。
验证 http://img7.imageshack.us/img7/5842/validating.gif
以下内容验证成功:
XHTML 输入
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xml:lang="en">
<head>
<title>Test</title>
</head>
<body>
<h1>Test</h1>
<p>This is a test</p>
</body>
</html>
如果我像 This is a test 那样中断我的段落,那么我在尝试处理响应 XML 时会收到此异常:
名称不能以“”开头 字符,十六进制值 0x22。 第 86 行,位置 40。
XML 异常 http://img11.imageshack.us/img11/ 3066/namecannotbegin.gif
如果验证连续两次失败,那么我似乎不能只修复我的段落标签并像平常一样继续。 由于某种原因,每个后续验证都会失败并出现以下异常:
操作超时
超时 http://img21.imageshack.us/img21/7600 /timedout.gif
这很奇怪。
我很抱歉发布我的整个项目,但我不知道我的问题来自哪里。 可能是我的线程、Web 服务通信、异常处理......我似乎找不到它。 我是否正确关闭了 StreamWriter、HttpWebRequest 和 ResponseStreams?
XAML
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="W3C Validation"
Height="300"
Width="300"
Name="Window1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0"
TextWrapping="Wrap"
AcceptsReturn="True"
VerticalScrollBarVisibility="Visible"
FontFamily="Consolas"
TextChanged="TextBox_TextChanged" />
<StatusBar Grid.Row="1">
<StatusBarItem>
<TextBlock x:Name="TextBlockResult" />
</StatusBarItem>
</StatusBar>
</Grid>
</Window>
Visual Basic
Imports System.ComponentModel
Imports <xmlns:env="http://www.w3.org/2003/05/soap-envelope">
Imports <xmlns:m="http://www.w3.org/2005/10/markup-validator">
Class Window1
Private WithEvents Worker As BackgroundWorker
Private _WorkerArgument As String
Private Sub Window1_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
InitializeWorker()
End Sub
Private Sub InitializeWorker()
Worker = New BackgroundWorker
Worker.WorkerSupportsCancellation = True
AddHandler Worker.DoWork, AddressOf Worker_DoWork
AddHandler Worker.RunWorkerCompleted, AddressOf Worker_RunWorkerCompleted
End Sub
Private Sub TextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.TextChangedEventArgs)
TryToWork(DirectCast(sender, TextBox).Text)
End Sub
Sub TryToWork(ByVal Argument As String)
If _WorkerArgument IsNot Nothing Then
_WorkerArgument = Argument
Exit Sub
End If
If Not Worker.IsBusy Then
TextBlockResult.Text = "Validating..."
Worker.RunWorkerAsync(Argument)
Exit Sub
End If
_WorkerArgument = Argument
Worker.CancelAsync()
Dim RetryTimer As New Windows.Threading.DispatcherTimer
AddHandler RetryTimer.Tick, AddressOf RetryTicker
RetryTimer.Interval = New TimeSpan(1) '1 tick'
RetryTimer.Start()
End Sub
Sub RetryTicker(ByVal sender As Object, ByVal e As System.EventArgs)
If Not Worker.IsBusy Then
DirectCast(sender, Windows.Threading.DispatcherTimer).Stop()
TextBlockResult.Text = "Validating..."
Worker.RunWorkerAsync(_WorkerArgument)
_WorkerArgument = Nothing
End If
End Sub
Private Sub Worker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
'wait for one second'
Dim StartTime As DateTime = DateTime.Now()
While Now.Subtract(StartTime) < New TimeSpan(0, 0, 1)
If DirectCast(sender, BackgroundWorker).CancellationPending Then
e.Cancel = True
Exit Sub
End If
System.Threading.Thread.Sleep(New TimeSpan(0, 0, 0, 0, 100)) 'tenth of a second'
End While
'then validate'
e.Result = Validate(DirectCast(e.Argument, String))
End Sub
Private Function Validate(ByVal Text As String) As String
Try
Dim Url As String = "http://validator.w3.org/check"
Dim Post As String = "&fragment=" + Web.HttpUtility.UrlEncode(Text) + "&output=soap12"
Dim ResponseDocument As XDocument = XDocument.Load(New Xml.XmlTextReader(Communicate(Url, Post)))
If ResponseDocument.Root.<env:Body>.<m:markupvalidationresponse>.<m:validity>.Value = "true" Then
Return "Valid"
Else
Return "Invalid"
End If
Catch ex As Exception
Return ex.Message
End Try
End Function
Private Function Communicate(ByVal Url As String, ByVal Post As String) As System.IO.Stream
Dim Writer As System.IO.StreamWriter = Nothing
Dim Request As System.Net.HttpWebRequest = System.Net.WebRequest.Create(Url)
Request.Method = "POST"
Request.ContentLength = Post.Length
Request.ContentType = "application/x-www-form-urlencoded"
Request.Timeout = 2000 '2 seconds'
Try
Writer = New System.IO.StreamWriter(Request.GetRequestStream())
Writer.Write(Post)
Catch
Finally
If Not Writer Is Nothing Then
Writer.Close()
End If
End Try
Return Request.GetResponse.GetResponseStream()
End Function
Private Sub Worker_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
If Not e.Cancelled Then
TextBlockResult.Text = DirectCast(e.Result, String)
End If
End Sub
End Class
感谢您的帮助!
I'm using the W3C validation service to check that the text I type into a TextBox is valid markup.
It's almost working. But, under particular conditions my input results in an error and then endless timeout exceptions. I have to close an re-open the program to get it working again.
Please glance over my code and help me to solve this issue.
I've got a pretty simple WPF application with a TextBox and a StatusBar. The StatusBar updates as I type to let me know if my typed markup is or is not valid. So that I'm not hammering the service, validations occur only after one second or longer has elapsed with no keystrokes.
Invalid http://img9.imageshack.us/img9/3788/invalidr.gif
It StatusBar may show: "Validating...", "Valid", "Invalid", or--if there's been one--an exception's message.
Validating http://img7.imageshack.us/img7/5842/validating.gif
The following validates successfully:
XHTML Input
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xml:lang="en">
<head>
<title>Test</title>
</head>
<body>
<h1>Test</h1>
<p>This is a test</p>
</body>
</html>
If I break my paragraph like <p>This is a test</
then I get this exception while trying to process the response XML:
Name cannot begin with the '"'
character, hexadecimal value 0x22.
Line 86, position 40.
XML Exception http://img11.imageshack.us/img11/3066/namecannotbegin.gif
If validation fails like that twice in a row, then it seems I can't just fix my paragraph tags and continue on like normal. For some reason each subsequent validation fails with this exception:
The operation has timed out
Timed Out http://img21.imageshack.us/img21/7600/timedout.gif
This is very strange.
I'm sorry to post my whole project, but I don't know where my problem is coming from. It might be my threading, web service communication, exception handling... I just can't seem to find it. Am I closing my StreamWriter, HttpWebRequest, and ResponseStreams correctly?
XAML
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="W3C Validation"
Height="300"
Width="300"
Name="Window1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0"
TextWrapping="Wrap"
AcceptsReturn="True"
VerticalScrollBarVisibility="Visible"
FontFamily="Consolas"
TextChanged="TextBox_TextChanged" />
<StatusBar Grid.Row="1">
<StatusBarItem>
<TextBlock x:Name="TextBlockResult" />
</StatusBarItem>
</StatusBar>
</Grid>
</Window>
Visual Basic
Imports System.ComponentModel
Imports <xmlns:env="http://www.w3.org/2003/05/soap-envelope">
Imports <xmlns:m="http://www.w3.org/2005/10/markup-validator">
Class Window1
Private WithEvents Worker As BackgroundWorker
Private _WorkerArgument As String
Private Sub Window1_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
InitializeWorker()
End Sub
Private Sub InitializeWorker()
Worker = New BackgroundWorker
Worker.WorkerSupportsCancellation = True
AddHandler Worker.DoWork, AddressOf Worker_DoWork
AddHandler Worker.RunWorkerCompleted, AddressOf Worker_RunWorkerCompleted
End Sub
Private Sub TextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.TextChangedEventArgs)
TryToWork(DirectCast(sender, TextBox).Text)
End Sub
Sub TryToWork(ByVal Argument As String)
If _WorkerArgument IsNot Nothing Then
_WorkerArgument = Argument
Exit Sub
End If
If Not Worker.IsBusy Then
TextBlockResult.Text = "Validating..."
Worker.RunWorkerAsync(Argument)
Exit Sub
End If
_WorkerArgument = Argument
Worker.CancelAsync()
Dim RetryTimer As New Windows.Threading.DispatcherTimer
AddHandler RetryTimer.Tick, AddressOf RetryTicker
RetryTimer.Interval = New TimeSpan(1) '1 tick'
RetryTimer.Start()
End Sub
Sub RetryTicker(ByVal sender As Object, ByVal e As System.EventArgs)
If Not Worker.IsBusy Then
DirectCast(sender, Windows.Threading.DispatcherTimer).Stop()
TextBlockResult.Text = "Validating..."
Worker.RunWorkerAsync(_WorkerArgument)
_WorkerArgument = Nothing
End If
End Sub
Private Sub Worker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
'wait for one second'
Dim StartTime As DateTime = DateTime.Now()
While Now.Subtract(StartTime) < New TimeSpan(0, 0, 1)
If DirectCast(sender, BackgroundWorker).CancellationPending Then
e.Cancel = True
Exit Sub
End If
System.Threading.Thread.Sleep(New TimeSpan(0, 0, 0, 0, 100)) 'tenth of a second'
End While
'then validate'
e.Result = Validate(DirectCast(e.Argument, String))
End Sub
Private Function Validate(ByVal Text As String) As String
Try
Dim Url As String = "http://validator.w3.org/check"
Dim Post As String = "&fragment=" + Web.HttpUtility.UrlEncode(Text) + "&output=soap12"
Dim ResponseDocument As XDocument = XDocument.Load(New Xml.XmlTextReader(Communicate(Url, Post)))
If ResponseDocument.Root.<env:Body>.<m:markupvalidationresponse>.<m:validity>.Value = "true" Then
Return "Valid"
Else
Return "Invalid"
End If
Catch ex As Exception
Return ex.Message
End Try
End Function
Private Function Communicate(ByVal Url As String, ByVal Post As String) As System.IO.Stream
Dim Writer As System.IO.StreamWriter = Nothing
Dim Request As System.Net.HttpWebRequest = System.Net.WebRequest.Create(Url)
Request.Method = "POST"
Request.ContentLength = Post.Length
Request.ContentType = "application/x-www-form-urlencoded"
Request.Timeout = 2000 '2 seconds'
Try
Writer = New System.IO.StreamWriter(Request.GetRequestStream())
Writer.Write(Post)
Catch
Finally
If Not Writer Is Nothing Then
Writer.Close()
End If
End Try
Return Request.GetResponse.GetResponseStream()
End Function
Private Sub Worker_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
If Not e.Cancelled Then
TextBlockResult.Text = DirectCast(e.Result, String)
End If
End Sub
End Class
Thanks for any help!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
乍一看,我建议不要在每次文本更改时都尝试进行验证。 这可能而且确实会让人有点不知所措。 我担心这就是这里正在发生的事情。 做得很好,使其异步,因为这将防止它阻塞,但我也怀疑它隐藏了你的问题。
尝试让 TextChanged 事件设置一个标志来执行验证器,然后让 DoWork 方法在紧密的循环中运行来检查该标志。 这将允许它有时间完成并且不会感到困惑。 可能需要一两秒钟才能获得正确的数据,但不应锁定。
祝你好运。
On a first glance, I would recomend not trying to validate every time the text is changed. That can, and does, get a bit overwhelming. I fear that is what is going on here. Good job on making it Async as that will keep it from blocking but I also suspect it is hiding your issue.
Try having the TextChanged event set a flag to execute the validator and then just have the DoWork method run in a tight(ish) loop checking that flag. That will allow it time to complete and not get confused. It might take a second or two to get the correct data but shouldn't lock up.
Good luck.