Skip to Content

We are beginning to implement Gateway at our company, which most of our development is done in a .NET environment. With some new changes in GW SP03, there are a few new things which you must do during the request to perform a modifying (create, update, delete) request around the CSRF tokens. I wanted to share a basic example of how I was able to utilize the native data service client functionality within .NET to perform a Create request to a Gateway service. This particular example is around creation of a material in ECC using BOR objects.

I’m not going to go into the creation of the Gateway service, as I will assume this is already done.

Create a new C# project in Visual Studio. Once the project is created, we need to add the Gateway service to the project.

In Solution Explorer, right click on References then click on Add Service Reference. Once the window opens, add the service by putting the URL to the service in the Address box and click GO. You should see the service name in the Services box along with the collections available for the service. Assign it to a meaningful Namespace and click OK.

/wp-content/uploads/2012/04/2012_04_26_08_39_27_96671.png

You will now be able to use the native functionality for data services in .NET for your Gateway service.

For this example I created just a basic Windows forms application. I have a single form with a text box (to display error messages) and a button (to start the create process).

First off, create a few objects to be used in a couple different methods. These will be used to hold the data both received and sent to the Gateway service.

    public partial class Debug : Form
    {
        private string csrfToken = String.Empty;
        private string setCookie = String.Empty;
        private CookieContainer cookieJar;

Inside the button click method is where I have the following code to actually execute the create request.

Basically we check to see if we have the CSRF token stored already, if we don’t we have to make a HTTP request to the Gateway server to get the token. Once we have the token and the corresponding cookies, we can proceed with the modifying request.

private void button2_Click(object sender, EventArgs e)
        {
            // Setup network credentials object to be used for requests to Gateway server
            System.Net.NetworkCredential credentials = new System.Net.NetworkCredential(Properties.Settings.Default.GW_Username, Properties.Settings.Default.GW_Password);
           
            // Create Gateway objects
            z_material material = new z_material();
            Z_MATERIALS materialClient = new Z_MATERIALS(new Uri(Properties.Settings.Default.GW_Uri));
            materialClient.Credentials = credentials;
            // Check to see if we already have the CSRF token...if not, request it from the server.
            if (this.csrfToken == String.Empty)
            {
                HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(Properties.Settings.Default.GW_Uri);
                HttpWebResponse resp;
                // Add custom header request to fetch the CSRF token
                req.Credentials = credentials;
                req.Method = "GET";
                req.Headers.Add("X-CSRF-Token", "Fetch");
                               
                // Setup cookie jar to capture cookies coming back from Gateway server. These cookies are needed along with the CSRF token for modifying requests.
                cookieJar = new CookieContainer();
                req.CookieContainer = cookieJar;
                try
                {
                    resp = (HttpWebResponse)req.GetResponse();
                }
                catch (System.Net.WebException ex)
                {
                    // Add your error handling here
                    return;
                }
                catch (Exception ex)
                {
                    // Add your error handling here
                    return;
                }
                // Assign values from response to class variables.
                this.csrfToken = resp.Headers.Get("X-CSRF-Token");
                this.setCookie = resp.Headers.Get("Set-Cookie");
            }       
            // Assign values to objects being passed into Gateway request.
            material.material_id = "MIKETEST2";
// commented out rest of value assignment for brevity
            // Use native .NET data service client to build request to add item to collection (CREATE/POST)
            materialClient.AddToz_materialCollection(material);
            // Add event handler to add additional data to service request
            materialClient.SendingRequest += new EventHandler<System.Data.Services.Client.SendingRequestEventArgs>(materialClient_SendingRequest);
            // Post changes to GW server
            try
            {
                materialClient.SaveChanges();
            }
            catch (Exception ex)
            {
                //Add your error handling here
            } 
        }

As you can see we’ve added an event handler to the materialClient object to do some additional processing of the request before it’s sent to the server.

Here we are going to add the CSRF token to the request headers, along with setting the content type to be what the Gateway server is expecting. Also, we must include the cookies from the response above, or else you will receive an error from the Gateway server with something along the lines of “CSRF token validation failed”.

        void materialClient_SendingRequest(object sender, System.Data.Services.Client.SendingRequestEventArgs e)
        {
            // If we have a CSRF token, add it to the request headers. Make sure to set Content-Type in headers to correct value.
            if (this.csrfToken != String.Empty)              
                e.RequestHeaders.Add("x-csrf-token", this.csrfToken);
            e.Request.ContentType = "application/atom+xml";
            // Append cookies from response above to modifying request.
            CookieCollection cookies = cookieJar.GetCookies(new Uri(Properties.Settings.Default.GW_Uri));
            foreach (Cookie cookie in cookies)
            {
                e.RequestHeaders.Add("Cookie", cookie.ToString());
            }           
        }

And that’s it! As long as your Gateway service is setup properly and you’re passing in all the appropriate values, you will create your material in the ECC system. This is a very generic example, but hopefully it will help someone out there with the CSRF token validation.

Mike Bowen

To report this post you need to login first.

5 Comments

You must be Logged on to comment or reply to a post.

  1. Steven Tilashalski

    This looks great and I had high hopes for success.  However, in my environment I’m still getting the pesky 403 “CSRF token validation failed” messages.

    I ran a test using Fiddler and it looks like each WCF communication to the Gateway is creating a new SAP session.  Since CSRF tokens are session specific, I think that explains the error.

    Any ideas?  I’m stumped. 

    (0) 
  2. NP CESC
    (0) 
  3. Remy Samulski

    Dear Mike,

    SendingRequest is deprecated in 4.5. Do you have similar code for SendingRequest2? I’m having difficulties in transfering above code to the new Event.

    Best regards,

    Rémy

    (0) 
    1. Carsten Komnik

      Dear all,

      I also tried to use SendingRequest2 instead. First I failed but fubnally I did it. Here is my solution based on Mike’s example:

      First I wrote a private method to catch the token:

      private void _getTokenFromSAP()

      {

      if (this._csrfToken == String.Empty)

      {

      HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(_uri);

      HttpWebResponse resp;

      req.Credentials = this._credentials;

      req.Method = “GET”;

      req.Headers.Add(“X-CSRF-Token”, “Fetch”);

      this._cookieJar = new CookieContainer();

      req.CookieContainer = this._cookieJar;

      try

      {

      resp = (HttpWebResponse)req.GetResponse();

      }

      catch (System.Net.WebException ex)

      {

      Console.WriteLine(ex.Message.ToString());

      return;

      }

      catch (Exception ex)

      {

      Console.WriteLine(ex.Message.ToString());

      return;

      }

      this._csrfToken = resp.Headers.Get(“X-CSRF-Token”);

      }

      }


      As you can see, this is similar to Mike’s one.


      Than I wrote an Event Handler method to enrich the requests:

      private void _container_SendingRequest_Enhance(object sender, SendingRequest2EventArgs e)

      {

      String CookieString = “”;

      CookieCollection cookies;

      this._getTokenFromSAP();  //Get token

      if (this._csrfToken != String.Empty)

      {

      e.RequestMessage.SetHeader(“X-CSRF-Token”, this._csrfToken);

      cookies = this._cookieJar.GetCookies(this._uri);

      foreach (Cookie cookie in cookies)

      {

      CookieString = CookieString + “;” + cookie.ToString();

      }

      e.RequestMessage.SetHeader(“Cookie”, CookieString.Substring(1));

      }

      }

      Let’s take a deeper look into the FOREACH. When I first ported Mike code to SendRequest2, I simply wrote the cookies in the loop into the header. But the three cookies overwrote themselfs.

      Reason: See wikipedia for Cookie:

      The Server is sending

      Set-Cookie ticket=abcd

      Set-Cookie token=hshcuzs,.x

      Set-Cookie sapuser=hplattner

      If the client send a new request, the header contains only one cookie. so we have to concatenate the cookies to one line:

      Cookie ticket=abcd; token=hshcuzs,.x; sapuser=hplattner

      This is happening in my FOREACH!

      To use the function, please add the following line to your code.


      After Z_MATERIALS materialClient = new Z_MATERIALS(new Uri(Properties.Settings.Default.GW_Uri));

      Add:

      materialClient.SendingRequest2 += new EventHandler<System.Data.Services.Client.SendingRequest2EventArgs>(this._container_SendingRequest_Enhance);



      Hope2help


      Carsten

      (0) 

Leave a Reply