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

1
+1
-1

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(); } }

3 answers

1
+1
-1

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.

  1. Imports System.Net.Http
  2. Imports System.Net.Http.Headers
  3. Imports UZ.BPMN.Datatypes
  4. Imports Newtonsoft.Json
  5.  
  6. Public Class BPMEngineService
  7. Inherits HttpClient
  8. Private username, password As String
  9.  
  10. Public Sub New(username As String, password As String)
  11. DefaultRequestHeaders.Accept.Clear()
  12. DefaultRequestHeaders.Accept.Add(New MediaTypeWithQualityHeaderValue("application/json"))
  13.  
  14. Me.BaseAddress = New Uri("http://localhost:9080")
  15. Me.username = username
  16. Me.password = password
  17. End Sub
  18.  
  19. Public Function GetTasks(procs As BPMNProcess()) As BPMNTask()
  20. Dim result As BPMNTask() = Nothing
  21. Try
  22. Login()
  23. Dim url As String = "/bonita/API/bpm/humanTask?p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3d0"
  24. Dim response As HttpResponseMessage = GetAsync(url).Result
  25. Dim reponseData As String = response.Content.ReadAsStringAsync().Result
  26. HttpContext.Current.Trace.Write(response.ToString)
  27. HttpContext.Current.Trace.Write(reponseData)
  28. result = JsonConvert.DeserializeObject(reponseData, GetType(BPMNTask()))
  29. Catch ex As Exception
  30. HttpContext.Current.Trace.Write("Error contacting REST service: " & ex.ToString)
  31. Finally
  32. Logout()
  33. End Try
  34. Return result
  35. End Function
  36.  
  37. Public Function GetProcess(processID As String) As BPMNProcess
  38. Dim result As BPMNProcess = Nothing
  39. Try
  40. Login()
  41. Dim url As String = "/bonita/API/bpm/process/" & processID
  42. Dim response As HttpResponseMessage = GetAsync(url).Result
  43. Dim reponseData As String = response.Content.ReadAsStringAsync().Result
  44. result = JsonConvert.DeserializeObject(reponseData, GetType(BPMNProcess))
  45. Catch ex As Exception
  46. HttpContext.Current.Trace.Write("Error contacting REST service: " & ex.ToString)
  47. Finally
  48. Logout()
  49. End Try
  50. Return result
  51. End Function
  52.  
  53. Public Function StartCase(processID As String) As BPMNCase
  54. Dim result As BPMNCase = Nothing
  55. Try
  56. Login()
  57. Dim formContent As New List(Of KeyValuePair(Of String, String)) From {New KeyValuePair(Of String, String)("processDefinitionId", processID)}
  58. Dim url As String = "/bonita/bpm/case/"
  59. Dim response As HttpResponseMessage = MyBase.PostAsync(url, New FormUrlEncodedContent(formContent)).Result
  60. HttpContext.Current.Trace.Write(response.ToString)
  61. HttpContext.Current.Trace.Write(response.Headers.ToString())
  62.  
  63. Dim reponseData As String = response.Content.ReadAsStringAsync().Result
  64. result = JsonConvert.DeserializeObject(reponseData, GetType(BPMNCase))
  65. Catch ex As Exception
  66. HttpContext.Current.Trace.Write("Error contacting REST service: " & ex.ToString)
  67. Finally
  68. Logout()
  69. End Try
  70. Return result
  71. End Function
  72.  
  73. Public Function GetProcesses() As BPMNProcess()
  74. Dim result As BPMNProcess() = Nothing
  75. Try
  76. Login()
  77. Dim url As String = "/bonita/API/bpm/process?p=0&c=10"
  78. Dim response As HttpResponseMessage = GetAsync(url).Result
  79. Dim reponseData As String = response.Content.ReadAsStringAsync().Result
  80.  
  81. HttpContext.Current.Trace.Write(response.ToString)
  82. HttpContext.Current.Trace.Write("Process data", reponseData)
  83. result = JsonConvert.DeserializeObject(reponseData, GetType(BPMNProcess()))
  84. Catch ex As Exception
  85. HttpContext.Current.Trace.Write("Error contacting REST service: " & ex.ToString)
  86. End Try
  87. Return result
  88. End Function
  89.  
  90. Private Sub Login()
  91. Try
  92. 'Add form data
  93. Dim formContent As New List(Of KeyValuePair(Of String, String)) From {New KeyValuePair(Of String, String)("username", username),
  94. New KeyValuePair(Of String, String)("password", password)}
  95.  
  96. Dim response As HttpResponseMessage = MyBase.PostAsync("/bonita/loginservice", New FormUrlEncodedContent(formContent)).Result
  97.  
  98. 'Display as string
  99. HttpContext.Current.Trace.Write(response.ToString)
  100. HttpContext.Current.Trace.Write(response.Headers.ToString())
  101. Catch ex As Exception
  102. HttpContext.Current.Trace.Write("Error contacting REST service: " & ex.ToString)
  103. End Try
  104. End Sub
  105.  
  106. Private Sub Logout()
  107. Try
  108. 'Add form data
  109. Dim response As HttpResponseMessage = MyBase.GetAsync("/bonita/logoutservice").Result
  110.  
  111. 'Display as string
  112. HttpContext.Current.Trace.Write(response.ToString)
  113. HttpContext.Current.Trace.Write(response.Headers.ToString())
  114. Catch ex As Exception
  115. HttpContext.Current.Trace.Write("Error contacting REST service: " & ex.ToString)
  116. End Try
  117. End Sub
  118.  
  119. Public Overrides Function SendAsync(request As HttpRequestMessage, cancellationToken As Threading.CancellationToken) As Threading.Tasks.Task(Of HttpResponseMessage)
  120. Dim sb As New StringBuilder
  121. For i As Integer = 0 To request.Properties.Count - 1
  122. sb.AppendLine(request.Properties.Keys(i) & ": " & request.Properties.Values(i).ToString)
  123. HttpContext.Current.Trace.Write("BPMNEngine Request", sb.ToString)
  124. Dim result As Threading.Tasks.Task(Of HttpResponseMessage) = MyBase.SendAsync(request, cancellationToken)
  125. Return result
  126. End Function
  127. End Class

1
0
-1

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.

  1. <TestClass()> Public Class BonitaRestServiceTests
  2. Private client As New HttpClient With {.BaseAddress = New Uri("http://localhost:9080")}
  3.  
  4. <TestMethod()> Public Sub Login()
  5. 'Setup for JSON
  6. client.DefaultRequestHeaders.Accept.Clear()
  7. client.DefaultRequestHeaders.Accept.Add(New MediaTypeWithQualityHeaderValue("application/json"))
  8.  
  9. Try
  10. 'Add form data
  11. 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")}
  12.  
  13. Dim response As HttpResponseMessage = client.PostAsync("/bonita/loginservice", New FormUrlEncodedContent(formContent)).Result
  14.  
  15. 'Display as string
  16. Trace.WriteLine(response.ToString)
  17. Trace.WriteLine(response.Headers.ToString())
  18. Catch ex As Exception
  19. Assert.Fail("Error contacting REST service: " & ex.ToString)
  20. End Try
  21. End Sub
  22.  
  23. <TestMethod()> Public Sub GetProcesses()
  24. 'Setup for JSON
  25. Login()
  26.  
  27. Try
  28. Dim url As String = "/bonita/API/bpm/process?p=0&c=10"
  29. Dim response As HttpResponseMessage = client.GetAsync(url).Result
  30. Dim reponseData As String = response.Content.ReadAsStringAsync().Result
  31.  
  32. 'Display response
  33. Trace.WriteLine(response.ToString)
  34.  
  35. 'Display response data
  36. Trace.WriteLine(reponseData)
  37.  
  38. 'Convert to object
  39. Dim procs As BPMNProcess() = JsonConvert.DeserializeObject(reponseData, GetType(BPMNProcess()))
  40. For Each proc As BPMNProcess In procs
  41. Trace.WriteLine("Found process " & proc.DisplayName)
  42. Next
  43.  
  44. Catch ex As Exception
  45. Assert.Fail("Error contacting REST service: " & ex.ToString)
  46. End Try
  47. End Sub
  48.  
  49. <TestMethod()> Public Sub GetTasks()
  50. 'Setup for JSON
  51. Login()
  52.  
  53. Try
  54. Dim url As String = "/bonita/API/bpm/humanTask?p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3d0"
  55.  
  56. Dim response As HttpResponseMessage = client.GetAsync(url).Result
  57. Dim reponseData As String = response.Content.ReadAsStringAsync().Result
  58.  
  59. 'Display response
  60. Trace.WriteLine(response.ToString)
  61.  
  62. 'Display response data
  63. Trace.WriteLine(reponseData)
  64.  
  65. 'Convert to object
  66. Dim tasks As BPMNTask() = JsonConvert.DeserializeObject(reponseData, GetType(BPMNTask()))
  67. For Each t As BPMNTask In tasks
  68. Trace.WriteLine("Found task " & t.DisplayName)
  69. Next
  70. Catch ex As Exception
  71. Assert.Fail("Error contacting REST service: " & ex.ToString)
  72. End Try
  73. End Sub
  74.  
  75. End Class

1
0
-1

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:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net.Http;
  5. using System.Net.Http.Headers;
  6.  
  7. namespace BonitaTest
  8. {
  9. class Program
  10. {
  11. static void Main()
  12. {
  13. var baseAddress = new Uri("http://localhost:8080");
  14. using (var handler = new HttpClientHandler { UseCookies = false})
  15. {
  16. using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
  17. {
  18.  // This needs to go over as a dict.
  19. var formContent = new Dictionary<string,string>
  20. {
  21. {"username", "walter.bates"},
  22. {"password", "bpm"},
  23. {"redirect", "false"}
  24. };
  25.  
  26. // Login to Bonita. Convert the dict to form encoded content.
  27. var result = client.PostAsync("/bonita/loginservice", new FormUrlEncodedContent(formContent)).Result;
  28.  
  29. // Variable to hold the authentication cookie.
  30. var setCookie = string.Empty;
  31.  
  32. // Get the authentication cookie from the login request.
  33. foreach (var header in result.Headers.Where(header => header.Key == "Set-Cookie"))
  34. {
  35. foreach (var value in header.Value)
  36. {
  37. setCookie = value;
  38. break; // Stop after finding something..
  39. }
  40. break; // We only care about the first match.
  41. }
  42.  
  43. // Uh oh, something bad happened...
  44. if (setCookie.Trim().Equals(""))
  45. {
  46. throw new Exception("No authentication cookie from login call!");
  47. }
  48.  
  49. // Make sure the accept headers are empty.
  50. client.DefaultRequestHeaders.Accept.Clear();
  51. client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
  52.  
  53. var message = new HttpRequestMessage(HttpMethod.Get, "/bonita/API/bpm/process?p=0&c=10");
  54. message.Headers.Add("Cookie", setCookie);
  55.  
  56. // Get a list of processes.
  57. var result2 = client.SendAsync(message).Result;
  58.  
  59. // Make sure a success code was returned.
  60. result2.EnsureSuccessStatusCode();
  61. }
  62. }
  63.  
  64. }
  65. }
  66. }

I hope this helps...

Notifications