views:

129

answers:

3

Hello!

I have a stupid problem. An jQuery.ajax request return me a full HTML text as a string. I receive such response in an case of error on the server. The server give me an error description which I want to place inside of the corresponding place of my current page.

So now the question: I have a string contains full HTML document (which is not an XML!!! see <hr> element inside). I need to have for example only BODY part as a jQuery object. Then I could append it to the corresponding part of my page.

Here is an example of the string which I need to parse:

<html>
  <head>
    <title>The resource cannot be found.</title>
    <style>
      body {font-family:"Verdana";font-weight:normal;font-size: .7em;color:black;}
      p {font-family:"Verdana";font-weight:normal;color:black;margin-top: -5px}
      // ...
    </style>
  </head>

  <body bgcolor="white">
    <span><H1>Server Error in '/' Application.<hr width=100% size=1 color=silver></H1>
          <h2> <i>The resource cannot be found.</i> </h2></span>
    <font face="Arial, Helvetica, Geneva, SunSans-Regular, sans-serif ">

      <b> Description: </b>HTTP 404. The resource you are looking for ...bla bla....
      <br><br>

      <b> Requested URL: </b>/ImportBPImagesInfos/Repository.svc/GetFullProfilimageSw<br><br>

      <hr width=100% size=1 color=silver>

      <b>Version Information:</b>&nbsp;Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.1

    </font>

  </body>
</html>
<!--
[HttpException]: A public action method &#39;....
   at System.Web.Mvc.Controller.HandleUnknownAction(String actionName)
   at System.Web.Mvc.Controller.ExecuteCore()
   at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
   at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext)
   at System.Web.Mvc.MvcHandler.<>c__DisplayClass8.<BeginProcessRequest>b__4()
   at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass1.<MakeVoidDelegate>b__0()
   at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)
   at System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag)
   at System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult)
   at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
-->
+1  A: 

In case of an error, you could pass the entire HTML string to jQuery to build an internal representation of it:

var bodyHtml = $(entirePageHTML).find('body').html();

or

var errorMessage = $(entirePageHTML).find('body h1').text();
David Hedlund
it was my first idea also, but $(entirePageHTML).find('body') returns an empty jQuery object and $(entirePageHTML).find('body').html() returns null.
Oleg
+2  A: 

Another way to do this, without jQuery:

function getStupidErrorMessage(str) {
  var bodyTags = str.match(/<\/*body[^>]*>/gim);
  // returns an array
  // bodyTags[0] is body open, bodyTags[1] is body close
  // unless someone output the markup backwards :)
  bodyContents = str.slice(bodyTags[0].length,-(bodyTags[1].length));
  return bodyContents; // use as innerHTML of <body> 
}

If you need the attributes of the BODY tag, parse those as well.

Robusto
Thank you for advice. The idea is good, but `bodyTags[0].length` and `bodyTags[1].length` can not by used in `str.slice`. They produce wrong sub-string. `bodyTags.lastIndex` is OK as the last parameter of `str.slice`, but I don't yet found the correct value for the first one.
Oleg
OK! data.responseText.slice(str.indexOf(bodyTags[0]),bodyTags.lastIndex) work!
Oleg
@Oleg: Glad it works for you, but it seems to me that str.indexOf(bodyTags[0]) would just return 0. So would you not be getting the contents *including* the open <body> tag? I thought you didn't want that.
Robusto
@Robusto: `str.match(/<\/*body[^>]*>/gim)` on my test string `str` produce array where `bodyTags[0]` is `'<body bgcolor="white">'` and `bodyTags[1]` is `'</body>'`. So `str.indexOf(bodyTags[0]) + bodyTags[0].length` gives me the index of contain after `<body bgcolor="white">` inside of my `str` and `bodyTags.lastIndex` is the same as `str.lastIndexOf(bodyTags[0])` or `str.lastIndexOf('</body>')`. So it works. Now I have two version which works. I choosed `exec` version of Sean Kinsey because it case insensitive and produce body content in one statement. Nevertheless thank you very much.
Oleg
+2  A: 

And the must-have non-jQuery answer:

 var bodyHtml = /<body.*?>([\s\S]*)<\/body>/.exec(entirePageHTML)[1];

This will return only whats inside the body tags.

UPDATE this accepts the attributes set on the body tag

Sean Kinsey
Thank you for the advice, but I receive only exception in the first and in the last version of this exprestion
Oleg
Take a closer look at the `body` tag of the response string. Your pattern will not match the opening tag.
patrick dw
Thanks, it has been fixed.
Sean Kinsey
Thank you! It works now!
Oleg