C# - Using REST API to get list of Processes - 401 Unauthorized

I am trying to use Bonita Web API. I My code is below. As you can see I call the loginservice before calling any other API service. It logs in OK 200. But when I make the subsequent call to get the list of processes I get a 401 error. You get a JSESSIONID from the first call and you are suppose to pass it to the subsequent calls to authenticate you. I’m using Bonita 6.3

var baseAddress = new Uri();
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
HttpResponseMessage result = client.PostAsync(“/bonita/loginservice”, new StringContent(“login=,password=,redirect=false”)).Result;
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json”));
HttpResponseMessage result2 = client.GetAsync(“/bonita/API/bpm/process”).Result;
result2.EnsureSuccessStatusCode();
}
}

1 Like

I was able to get this working in c#. I removed the cookie container and set the cookie directly in the header but you may be able to get the cookie container working. I didn’t see you setting the authentication cookie in the cookie container, and I believe your login was failing (even though you received a 200 response and a jsessionid) because you were not form encoding the username, password, etc.

Here is the code which works on my end. This is a console project:

using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Net.Http.Headers;

namespace BonitaTest
{
class Program
{
static void Main()
{
var baseAddress = new Uri(“http://localhost:8080”);
using (var handler = new HttpClientHandler { UseCookies = false})
{
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
 // This needs to go over as a dict.
var formContent = new Dictionary<string,string>
{
{“username”, “walter.bates”},
{“password”, “bpm”},
{“redirect”, “false”}
};

                // Login to Bonita. Convert the dict to form encoded content.
                var result = client.PostAsync("/bonita/loginservice", new FormUrlEncodedContent(formContent)).Result;
                
                // Variable to hold the authentication cookie.
                var setCookie = string.Empty;

                // Get the authentication cookie from the login request.
                foreach (var header in result.Headers.Where(header => header.Key == "Set-Cookie"))
                {
                    foreach (var value in header.Value)
                    {
                        setCookie = value;
                        break; // Stop after finding something..
                    }
                    break; // We only care about the first match.
                }

                // Uh oh, something bad happened...
                if (setCookie.Trim().Equals(""))
                {
                    throw new Exception("No authentication cookie from login call!");
                }

                // Make sure the accept headers are empty.
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                var message = new HttpRequestMessage(HttpMethod.Get, "/bonita/API/bpm/process?p=0&c=10");
                message.Headers.Add("Cookie", setCookie);

                // Get a list of processes.
                var result2 = client.SendAsync(message).Result;

                // Make sure a success code was returned.
                result2.EnsureSuccessStatusCode();
            }
        } 

    }
}

}

I hope this helps…

Hello,

there is no need to read the Set-Cookie in .NET, theoretically you can’t even read it as the TomCat server posts it as ‘httpOnly’ (tried this and I couldn’t!). Turning httpOnly off on the TomCat server makes the site unsafe and doesn’t help when reading the headers in client-side code , e.g. using AngularJS as the browser XHR does not read Set-Cookie or Set-Cookie2, ever!

.NET will honor the Set-Cookie automatically, see my test code (VB).

A good trick is to use Json2CSharp to create json data classes for deserialisation.

Public Class BonitaRestServiceTests Private client As New HttpClient With {.BaseAddress = New Uri("http://localhost:9080")}

<TestMethod()> Public Sub Login()
'Setup for JSON
client.DefaultRequestHeaders.Accept.Clear()
client.DefaultRequestHeaders.Accept.Add(New MediaTypeWithQualityHeaderValue(“application/json”))

Try
  'Add form data
  Dim formContent As New List(Of KeyValuePair(Of String, String)) From {New KeyValuePair(Of String, String)("username", "walter.bates"), New KeyValuePair(Of String, String)("password", "bpm")}

  Dim response As HttpResponseMessage = client.PostAsync("/bonita/loginservice", New FormUrlEncodedContent(formContent)).Result

  'Display as string
  Trace.WriteLine(response.ToString)
  Trace.WriteLine(response.Headers.ToString())
Catch ex As Exception
  Assert.Fail("Error contacting REST service: " & ex.ToString)
End Try

End Sub

<TestMethod()> Public Sub GetProcesses()
'Setup for JSON
Login()

Try
  Dim url As String = "/bonita/API/bpm/process?p=0&c=10"
  Dim response As HttpResponseMessage = client.GetAsync(url).Result
  Dim reponseData As String = response.Content.ReadAsStringAsync().Result

  'Display response
  Trace.WriteLine(response.ToString)

  'Display response data
  Trace.WriteLine(reponseData)

  'Convert to object
  Dim procs As BPMNProcess() = JsonConvert.DeserializeObject(reponseData, GetType(BPMNProcess()))
  For Each proc As BPMNProcess In procs
    Trace.WriteLine("Found process " & proc.DisplayName)
  Next

Catch ex As Exception
  Assert.Fail("Error contacting REST service: " & ex.ToString)
End Try

End Sub

<TestMethod()> Public Sub GetTasks()
'Setup for JSON
Login()

Try
  Dim url As String = "/bonita/API/bpm/humanTask?p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3d0"

  Dim response As HttpResponseMessage = client.GetAsync(url).Result
  Dim reponseData As String = response.Content.ReadAsStringAsync().Result

  'Display response
  Trace.WriteLine(response.ToString)

  'Display response data
  Trace.WriteLine(reponseData)

  'Convert to object
  Dim tasks As BPMNTask() = JsonConvert.DeserializeObject(reponseData, GetType(BPMNTask()))
  For Each t As BPMNTask In tasks
    Trace.WriteLine("Found task " & t.DisplayName)
  Next
Catch ex As Exception
  Assert.Fail("Error contacting REST service: " & ex.ToString)
End Try

End Sub

End Class

Hi guys,

thanks for the answers, but this actually works out of the box in .NET, there is absolutely no need to set cookies.

I am now using the new httpClient in .Net 4.5
I have created the service layer objects using XamaSofts JSOn to object convertor using cut and paste of the Bonita examples, are from a network monitor. Sure would be much nice if Bonita would provide decent schemas. Also, they really need to clean up their web api, the naming conventions (or more precisly complete lack thereof) are a mess.

Imports System.Net.Http
Imports System.Net.Http.Headers
Imports UZ.BPMN.Datatypes
Imports Newtonsoft.Json

Public Class BPMEngineService
Inherits HttpClient
Private username, password As String

Public Sub New(username As String, password As String)
DefaultRequestHeaders.Accept.Clear()
DefaultRequestHeaders.Accept.Add(New MediaTypeWithQualityHeaderValue(“application/json”))

Me.BaseAddress = New Uri("http://localhost:9080")
Me.username = username
Me.password = password

End Sub

Public Function GetTasks(procs As BPMNProcess()) As BPMNTask()
Dim result As BPMNTask() = Nothing
Try
Login()
Dim url As String = “/bonita/API/bpm/humanTask?p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3d0”
Dim response As HttpResponseMessage = GetAsync(url).Result
Dim reponseData As String = response.Content.ReadAsStringAsync().Result
HttpContext.Current.Trace.Write(response.ToString)
HttpContext.Current.Trace.Write(reponseData)
result = JsonConvert.DeserializeObject(reponseData, GetType(BPMNTask()))
Catch ex As Exception
HttpContext.Current.Trace.Write("Error contacting REST service: " & ex.ToString)
Finally
Logout()
End Try
Return result
End Function

Public Function GetProcess(processID As String) As BPMNProcess
Dim result As BPMNProcess = Nothing
Try
Login()
Dim url As String = “/bonita/API/bpm/process/” & processID
Dim response As HttpResponseMessage = GetAsync(url).Result
Dim reponseData As String = response.Content.ReadAsStringAsync().Result
result = JsonConvert.DeserializeObject(reponseData, GetType(BPMNProcess))
Catch ex As Exception
HttpContext.Current.Trace.Write("Error contacting REST service: " & ex.ToString)
Finally
Logout()
End Try
Return result
End Function

Public Function StartCase(processID As String) As BPMNCase
Dim result As BPMNCase = Nothing
Try
Login()
Dim formContent As New List(Of KeyValuePair(Of String, String)) From {New KeyValuePair(Of String, String)(“processDefinitionId”, processID)}
Dim url As String = “/bonita/bpm/case/”
Dim response As HttpResponseMessage = MyBase.PostAsync(url, New FormUrlEncodedContent(formContent)).Result
HttpContext.Current.Trace.Write(response.ToString)
HttpContext.Current.Trace.Write(response.Headers.ToString())

  Dim reponseData As String = response.Content.ReadAsStringAsync().Result
  result = JsonConvert.DeserializeObject(reponseData, GetType(BPMNCase))
Catch ex As Exception
  HttpContext.Current.Trace.Write("Error contacting REST service: " & ex.ToString)
Finally
  Logout()
End Try
Return result

End Function

Public Function GetProcesses() As BPMNProcess()
Dim result As BPMNProcess() = Nothing
Try
Login()
Dim url As String = “/bonita/API/bpm/process?p=0&c=10”
Dim response As HttpResponseMessage = GetAsync(url).Result
Dim reponseData As String = response.Content.ReadAsStringAsync().Result

  HttpContext.Current.Trace.Write(response.ToString)
  HttpContext.Current.Trace.Write("Process data", reponseData)
  result = JsonConvert.DeserializeObject(reponseData, GetType(BPMNProcess()))
Catch ex As Exception
  HttpContext.Current.Trace.Write("Error contacting REST service: " & ex.ToString)
End Try
Return result

End Function

Private Sub Login()
Try
'Add form data
Dim formContent As New List(Of KeyValuePair(Of String, String)) From {New KeyValuePair(Of String, String)(“username”, username),
New KeyValuePair(Of String, String)(“password”, password)}

  Dim response As HttpResponseMessage = MyBase.PostAsync("/bonita/loginservice", New FormUrlEncodedContent(formContent)).Result

  'Display as string
  HttpContext.Current.Trace.Write(response.ToString)
  HttpContext.Current.Trace.Write(response.Headers.ToString())
Catch ex As Exception
  HttpContext.Current.Trace.Write("Error contacting REST service: " & ex.ToString)
End Try

End Sub

Private Sub Logout()
Try
'Add form data
Dim response As HttpResponseMessage = MyBase.GetAsync(“/bonita/logoutservice”).Result

  'Display as string
  HttpContext.Current.Trace.Write(response.ToString)
  HttpContext.Current.Trace.Write(response.Headers.ToString())
Catch ex As Exception
  HttpContext.Current.Trace.Write("Error contacting REST service: " & ex.ToString)
End Try

End Sub

Public Overrides Function SendAsync(request As HttpRequestMessage, cancellationToken As Threading.CancellationToken) As Threading.Tasks.Task(Of HttpResponseMessage)
Dim sb As New StringBuilder
For i As Integer = 0 To request.Properties.Count - 1
sb.AppendLine(request.Properties.Keys(i) & ": " & request.Properties.Values(i).ToString)
Next
HttpContext.Current.Trace.Write(“BPMNEngine Request”, sb.ToString)
Dim result As Threading.Tasks.Task(Of HttpResponseMessage) = MyBase.SendAsync(request, cancellationToken)
Return result
End Function
End Class