cancel
Showing results for 
Search instead for 
Did you mean: 
Read only

Need to replicate short php code in BSP

Former Member
0 Likes
1,542

HI All,

First off I know nothing about BSP, just thought I would get that one out the way.

With that said I have tried to follow this wiki :- http://wiki.sdn.sap.com/wiki/display/BSP/Handling+Binary+Data with some success.

Now what I am trying to achieve is simply reproduce this small piece of php code :-

<?php

header("Content-type: application/octet-stream");

header("Content-Disposition: attachment; filename=\"my-data.csv\"");

$data=stripcslashes($_REQUEST['csv_text']);

echo $data;

?>

in a BSP application.

I have tried the following in the layout tab and in the Event OnInitialization:-

<%

   IF request->get_form_field( 'csv_text' ) IS NOT INITIAL.

      response->set_header_field( name = 'Content-Type'        value = 'application/octetstream' ).

      response->set_header_field( name = 'Content-Disposition' value = 'attachment; filename=csv_text.csv' ).

      response->set_header_field( name = 'Connection'          value = 'close' ).

       DATA: file_content TYPE string. file_content = request->get_form_field( 'csv_text' ).

      response->set_header_field( name = 'Expires'       value = '0' ).

      response->set_header_field( name = 'Pragma'        value = 'no-cache' ).

      response->set_header_field( name = 'Cache-Control' value = 'max-age=0' ).

      REPLACE ALL OCCURRENCES OF ' ' IN file_content WITH ''.

      response->set_cdata( file_content ).

      RETURN.

   ENDIF.

%>

The only problem is that I get the desired download dialog box but I am directed to a blank BSP screen with "Your request is being processed" message instead of simply leaving me on my current page and showing a download dialog like with PHP.

I am POSTING some CSV data from a table page in SAP BW to this BSP application that I want to download as a CSV text file.

I can't simply use the export to CSV function of SAP BW because of the modifications I have made in javascript to change the tables.

I am utilising this piece http://www.kunalbabre.com/projects/table2CSV.php  of jQuery utility to get what I require from the client side.  Option 3.1 is what I am trying to achieve.

Any help please?

Thanks

Craig

Accepted Solutions (0)

Answers (2)

Answers (2)

Former Member
0 Likes

Rüdiger

I tried it outside of BW and sure enough it works as expected.

I have also discovered why it's not working properly, but I don't know how to resolve it.

Basically my form action is set to "../bc/bsp/sap/zsavecsv/download.htm" but when you submit it you end up with something like this "/sap(bD1lbiZjPTIwMA==)/bc/bsp/sap/zsavecsv/download.htm" in the form that's generated.  Presumably this is the session encode in the URL.

I have however managed to cheat and use javascript XMLHttpRequest to initially get the request and get the BSP page to send me the "runtime->PAGE_URL" as the responseText. I then update the form action with the returned value and this works.

Can you think of an easier way of doing this?

Cheers

Craig

Ruediger_Plantiko
Active Contributor
0 Likes

Craig:

bD1lbiZjPTIwMA== is base64 for your client and language (decoded, it is l=en&c=200). It's not the session id but the essential login information that the SAP-System needs in addition to user and password. (If cookies are enabled, the session id is stored as a cookie and not in the URL.) They can also be passed as form fields  (sap-client and sap-language) in the request or in the URL query part.

ICF sends a redirect with this information "mangled" into the URL, immediately after the first node of the URL path. But this is a real redirect with HTTP-statuscode, not a form!

In my eyes, you still haven't solved the mystery of that form above, with the tell-tale text "No Applicable Data Found". I still would bet that this form doesn't come from the ICF or BSP framework!

Usually, when a relative URL is specified which doesn't replace the top level node, this information is preserved (since it's part of the top level node!), and no redirect is necessary. It's inconceivable to me why the system triggers such a redirect in your case.

Regards,

Rüdiger

Former Member
0 Likes

Rüdiger,

Ignore the "No Applicable Data Found" message as this is just part of the table content passed into the form value.  It was test data.

Essentially I believe it's the BSP framework generating the form after the redirect with the action set to the same URL but with the client and language mangled into the URL.  Nothing from BW is doing this since BW is not even aware of the posting.  The page I use to post the data is completely manipulated by javascript on the client.

I can only assume since the client and language are missing from the initial request it requires these to make the request and since it's receiving posted data it generates the form and submits it to itself.

EDIT

This is only when I call the service directly in a browser.

I can see that there are 2 calls when we make the posting

1st GET request to /sap/bc/bsp/sap/zsavecsv/download.htm receives status "302 Moved Temporarily" then the 2nd GET request to /sap(bD1lbiZjPTIwMA==)/bc/bsp/sap/zsavecsv/download.htm receives status "200 OK"

If I use the BW template and check the posting I see a POST request to /sap/bc/bsp/sap/zsavecsv/download.htm with a status of "200 OK".  And thats it no redirect occurs here.

I will test this outside of both BSP and BW to prove it's BSP doing it.

I'll come back with the results.

The results are, if I try to post a table of results from an html file locally the same problem occurs!

Basically BSP is generating this page for some reason.

How do I go about stopping the base64 encoding?  Or can we simply pass the language and client in the URL?

Cheers

Craig

Ruediger_Plantiko
Active Contributor
0 Likes

Hi Craig,

the two replies, the first with status 302, the second with 200, is precisely what is to expect. You can avoid the redirect by providing language and client with your first request. So the question is why the first request is addressed to /sap/bc... and not to /sap(...)/bc... If you specified a relative URL in your action, the /sap(...)/...  would be copied from the actual, current URL from which the form is submitted.

- Rüdiger

http://migzm630.migros.ch:8000/sap/bc/bsp/sap/zz_test_dl/submit.htm

Former Member
0 Likes

Rüdiger

Even if I specify the URL as /sap/bc/bsp/sap/zsavecsv/download.htm?sap-language=en&sap-client=200 it still doesn't work!

Normally I'd like to get to the bottom of a problem, but now I am losing the will to live and have a solution albeit it's a work around that works, I might just leave it.

Cheers

Craig

Former Member
0 Likes

Rüdiger,

Do me a favour and try the below in a local html file, obviously replacing the <host> and <port> etc.

<html>

  <head>

    <title>Test submit download</title>

  </head>

  <body>

    <h1>Test Submit Download</h1>

    <form method="post" action="<host>:<port>/sap/bc/bsp/sap/zsavecsv/download.htm">

      <input type="text" name="csv_text" value="a;b;c">

      <input type="submit" value="Submit">

    </form>

  </body>

</html>

This reproduces the problem I am having.

Perhaps it's a system thing, we are running BW 7 (EHP1).

Cheers

Craig

Ruediger_Plantiko
Active Contributor
0 Likes

Craig,

with the HTML file as local file, I can reproduce your problem - now I get the suspicious form, too.

So you are right, it comes from the ICF! It appears only when the mangling part of the URL is missing, and when the method is "POST", not "GET". With "GET", the system behaves differently, passing back an HTTP 302 Redirect in order to navigate to the mangled URL.

When the requested URL contains the (...) part, no redirect or hidden form is sent, but the request is served directly.

But an URL beginning with /sap/bc is absolute, not relative! What about passing a relative URL? Then the foremost parts of the URL are taken from the URL that is currently visible in the browser's address bar. Since you are on the same system, the mangling part will be copied from there, and your request can be served directly.

If your current path begins with /sap(...)/bw/BEx, say, then by starting with sufficiently many "../", you can make the browser copy the first node, including the bracket part (which is part of the first node, syntactically), and then going down to .../bc/bsp/sap/... instead.

Alternatively, at "onload", you could copy the current mangling string into the action attribute of the form.

You could rewrite the URL even earlier, in the view that generates the form, by using the CL_HTTP_UTILITY=>IF_HTTP_UTILITY~REWRITE_URL (which accepts a table of name/value-pairs)

But the easiest approach will probably be using the relative URL (i.e. a URL starting with a certain number of '../' or './') for the form's action attribute.

Regards,
Rüdiger

Former Member
0 Likes

Rüdiger,

Thanks for your invaluable input.

But the problem I have is I am using the relative URL in my BW template, issue becomes the fact that the mangled URL of the BSP has no equivalent in SAP BW.  BW contains the session ID in that part of the URL!

I'll stick with my current solution.

Thanks very much for your time.

Cheers

Craig

Ruediger_Plantiko
Active Contributor
0 Likes

Hi Craig,

by looking at the code, I don't see an obvious error.

My only concern is about the field Content-Type. I am not sure whether this won't be overwritten by the ICF if you set it with the general set_header_field method. There is a special method set_content_type for it, which I would prefer.

If it still doesn't work: Did you switch on the "Network log" in Firebug (or in MSIE: <F12>), and compared the response headers with your expectations? Which header field values were actually sent in the response?

Regards,

Rüdiger

BTW: Did you know there is an interface IF_HTTP_HEADER_FIELDS containing constants for all specified header field names? Using these constants (like IF_HTTP_HEADER_FIELDS=>CACHE_CONTROL ) instead of literal values may be helpful for avoiding mistyping.

Former Member
0 Likes

Thanks for the response.  I changed the BSP to use set_header_field but have same problem.

Below is the response headers :-

Response Headers

Content-Length    27183

Content-Type    text/html; charset=iso-8859-1

Server    SAP NetWeaver Application Server / ABAP 701

Request Headersview source

Accept    text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Encoding    gzip, deflate

Accept-Language    en-gb,en;q=0.5

Connection    keep-alive

Cookie    sap-usercontext=sap-language=EN; MYSAPSSO2=AjExMDABAAxBUk1TVEVDICAgICACAAMyMDADAAhCV0QgICAgIAQADDIwMTIwNzAyMTMyOAUABAAAAAgGAAFYCQABRf8A9DCB8QYJKoZIhvcNAQcCoIHjMIHgAgEBMQswCQYFKw4DAhoFADALBgkqhkiG9w0BBwExgcAwgb0CAQEwEzAOMQwwCgYDVQQDEwNCV0QCAQAwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTEyMDcwMjEzMjg1N1owIwYJKoZIhvcNAQkEMRYEFKHKk9CO4BE7BO07gr6jG1tal2mBMAkGByqGSM44BAMELjAsAhQPuFOoGUSIqlIrxSFJconeraYWyQIUCawlv5WI48vy06f%2fMrbjnJPL%2f9o%3d

Host    xxxx:8008

Referer    http://xxxx:8008/sap/bw/BEx?SAP-LANGUAGE=EN&PAGENO=3&CMD=PROCESS_VARIABLES&REQUEST_NO=0&CMD=PROCESS_...

User-Agent    Mozilla/5.0 (Windows NT 5.1; rv:13.0) Gecko/20100101 Firefox/13.0.1

Response Headers From Cache

Content-Length    27183

Content-Type    text/html; charset=iso-8859-1

Server    SAP NetWeaver Application Server / ABAP 701

Request Headers From Upload Stream

Content-Length    24185

Content-Type    application/x-www-form-urlencoded

Any further suggestions?

Thanks

Craig

Ruediger_Plantiko
Active Contributor
0 Likes

Well, it's difficult to analyze from remote.

But where is the content-type 'application/octet-stream' that you had set?

BTW, shouldn't it be "application/octet-stream" with a hyphen ? In your BSP code, you write 'application/octetstream' without hyphen. 

I don't see the MIME type that you had set in the header field list that you had sent (which is a mix of request and response header fields, anyway).

Did you put a break-point on the ->set_content_type( ... ) statement? Does it reach that statement at all?

Former Member
0 Likes

Lol yes you are correct my mistake I had forgotten the hyphen, but it didn't help anyway.

I have just put a break point on the ->set_content_type but the page is already generated at this point!

It does go through the motions and sets the headers correctly that's when I get the download dialog.

But the page is already generated at this point with the data in it with a form :-

<html>

    <head>

        <style type="text/css">.middle  { vertical-align:middle; }</style>

    </head>

    <body onload="document.forms[0].submit()">

        <form method="post" action="/sap(bD1lbiZjPTIwMA==)/bc/bsp/sap/zsavecsv/savefile.bsp">

        <input type="hidden" name="csv_text" value="&amp;nbsp;,&quot;No Applicable Data Found.&quot;

        &amp;nbsp;,&quot;No Applicable Data Found.&quot;

        &amp;nbsp;,&quot;&amp;nbsp;&quot;

        &amp;nbsp;,&quot;No Applicable Data Found.&quot;

        &amp;nbsp;,&quot;&amp;nbsp;&quot;>

        </form>

            <table border="0" width="100%" height="100%">

                <tr>

                    <td align="center" class="middle">Your request is being processed</td>

                </tr>

            </table>

    </body>

</html>

So it would seem my page  already has a generated html page before my code even kicks in!

Ruediger_Plantiko
Active Contributor
0 Likes

Maybe it's because you are in a page, not in a controller. Probably, the MIME type proposed in the page properties (see screenshot below) will finally be set from the framework.

The more natural approach would be to use a controller or even an SICF request handler for your task. But if you set the MIME type for your page in the page attributes, I don't see why it sholdn't work in your way.

Regards,

Rüdiger

Ruediger_Plantiko
Active Contributor
0 Likes

So you use the same page for two purposes? The download and the form?

Wouldn't it be better to handle the download with a separate item? In particular, since you have different MIME types for the form and the download.

With the "action" attribute of the form page you can specify the controller or page that should take care of the download.

We once had a similar thing (but for uploads), and we realized it with a separate controller. It came out that the controller even was reusable for other cases later on, for which we had no idea when the first app had been implemented.

Former Member
0 Likes

Thanks for the response, but the form I didn't create.  There is nothing else in my page other than what I posted.  Something else is generating this.  If a controller is required how to go about creating that?

Edit

I've just checked and the MIME type for the page is set to application/octet-stream

Thanks

Craig

Ruediger_Plantiko
Active Contributor
0 Likes

Hi Craig,

I just created a page with flow logic with the following sole content:

<%

      response->set_header_field( name = 'Content-Type'        value = 'application/octet-stream' ). 

      response->set_header_field( name = 'Content-Disposition' value = 'attachment; filename=csv_text.csv' ). 

      response->set_header_field( name = 'Connection'          value = 'close' ). 

 

      response->set_header_field( name = 'Expires'       value = '0' ). 

      response->set_header_field( name = 'Pragma'        value = 'no-cache' ). 

      response->set_header_field( name = 'Cache-Control' value = 'max-age=0' ).  

 

      response->set_cdata( 'a;b;c' ). 

%>

It behaves as expected: When I call it, Firefox asks me whether I want to open the csv file with MS Excel, or simply save the file. When I choose "open", excel is started, with the first three cells of the sheet filled with 'a', 'b', 'c'.

Could you try to reduce your code this way and see whether it behaves the same way?

This could give you a hint on what's going wrong.

Rüdiger.

Former Member
0 Likes

Rüdiger

Yes that does what I want my page to do but with me sending it some data.

How to progress from here?

Cheers

Craig

Former Member
0 Likes

Rüdiger,

Ignore me I'm being an idiot, I changed the form from post to get and it worked.

It didn't dawn on me that this might have such an impact.  I simply copied the form from the example given.

Thanks for your time.

Craig

Ruediger_Plantiko
Active Contributor
0 Likes

Hi Craig,

never mind 🙂

If I understand you correctly, the problem was outside of the code you displayed to us, in a HTML form in a previous stage of the app.

Just to finish this discussion: I am curious if you could explain the root cause of the problem in a few words? request->get_form_field( ) works just the same for GET as well as for POST requests. So what was the problem?

Rüdiger

Former Member
0 Likes

Ok here goes.

I had this form in a BW Web template :-

<FORM method="post" action="/sap/bc/bsp/sap/zsavecsv/savefile.bsp">

  <input type="hidden" name="csv_text" id="csv_text"><input type="submit" value="Get CSV File" onclick="getCSVData()">

</FORM>

The getCSVData() javascript function (extension of jQuery) was simply this:-

function getCSVData(){

  var csv_value=$('#TABLE_1').table2CSV({delivery:'value'});

  $("#csv_text").val(csv_value);

}

The BSP page within the layout tab was/is :-

<%

  IF request->get_form_field( 'csv_text' ) IS NOT INITIAL.

  DATA: output TYPE string ,

  app_type TYPE string ,

  l_xstring TYPE xstring.

  DATA: file_content TYPE string. file_content = request->get_form_field( 'csv_text' ).

  REPLACE ALL OCCURRENCES OF ' ' IN file_content WITH ''.

  response->set_header_field( name = 'Content-Type'        value = 'application/octet-stream' ).

  response->set_header_field( name = 'Content-Disposition' value = 'attachment; filename=csv_text.csv' ).

  response->set_header_field( name = 'Connection'          value = 'close' ).

  response->set_header_field( name = 'Expires'       value = '0' ).

  response->set_header_field( name = 'Pragma'        value = 'no-cache' ).

  response->set_header_field( name = 'Cache-Control' value = 'max-age=0' ).

  response->set_cdata( file_content ).

  ENDIF.

%>

The problem: with the form method set to POST, the BSP page had generated by the system (nothing to do with me) a form that submitted to itself with the content of the POSTED field csv_text (plain csv data), then prompted to save the content with a download dialog box.  Leaving you with essentially a blank page.

Simply changing the form method to GET the pre-generated form page doesn't get generated and you simply get what I wanted in the first place, a download dialog.

Hope this makes sense!  Well it doesn't really, since I can't understand why the form page get generated in the first place.

Cheers

Craig

Former Member
0 Likes

OK now I am annoyed, just when you think you have cracked it.

This works in firefox but IE refuses to play ball.

Opens a page with this in the URL :-

/sap/bc/bsp/sap/zsavecsv/savefile.bsp?csv_text=%26nbsp%3B%2C%22No+Applicable+Data+Found.%22%0D%0A%26nbsp%3B%2C%22No+Applicable+Data+Found.%22%0D%0A%26nbsp%3B%2C%22%26nbsp%3B%22%0D%0A%26nbsp%3B%2C%22No+Applicable+Data+Found.%22%0D%0A%26nbsp%3B%2C%22%26nbsp%3B%22%0D%0A%22Channel%22%2C%22%26nbsp%3B%22

But with a lot more on the end which I assume is simply the csv data and refuses to open saying "Internet Explorer cannot display the webpage"

I assume there is an upper limit to a URL string!

Any idea?

Cheers

Craig

Former Member
0 Likes

OK I know why IE doesn't work, according to this http://support.microsoft.com/kb/208427 Maximum URL length is 2,083 characters in Internet Explorer.

So I need to get the POST method working since name/value pairs are transferred in the header and not in the URL.

Help! 😞

Ruediger_Plantiko
Active Contributor
0 Likes

Craig,

I have the suspect that BEx (or something in the calling app) is producing the intermediate self-submitting form, it's not, as you think, the BSP framework.

Proof: I created a second BSP page submit.htm with the following content

<html>

  <head>

    <title>Test submit download</title>

  </head>

  <body>

    <h1>Test Submit Download</h1>

    <form method="post" action="download.htm">

      <input type="text" name="csv_data" value="a;b;c">

      <input type="submit" value="Submit">

    </form>

  </body>

</html>

When calling submit.htm and hitting the button, the download page is called, which I changed like this:

<%  data: lv_csv type string.

    lv_csv = request->get_form_field('csv_data').

    if lv_csv is not initial.

      response->set_header_field( name = 'Content-Type'        value = 'application/octet-stream' ).

      response->set_header_field( name = 'Content-Disposition' value = 'attachment; filename=csv_text.csv' ).

      response->set_header_field( name = 'Connection'          value = 'close' ).

      response->set_header_field( name = 'Content-Lentgh'      value = '0' ).

      response->set_header_field( name = 'Expires'       value = '0' ).

      response->set_header_field( name = 'Pragma'        value = 'no-cache' ).

      response->set_header_field( name = 'Cache-Control' value = 'max-age=0' ).

      response->set_cdata( lv_csv ).

      endif.%>

The page behaves as before, as expected.

This proves that BSP receives the form parameter csv_data without any problems.

Regards,

Rüdiger

Former Member
0 Likes

Rüdiger

Thanks again for coming back to me.  I will try this outside of BW Web Application and replicate as you have directly in BSP.  Unfortunately I think I've killed our DEV box so it'll have to wait until the morning.

I'll let you know how I get on.

Cheers

Craig