Building out first client using ServiceStack - servicestack

I'm trying out ServiceStack for connecting to an OAuth2 / JSON service. (C#)
First I'm struggling to find a good example of this out there as it appears most examples are using v3 of ServiceStack. So if you're aware of a good example to work off of that would be great.
I'm working with the example code on SO question: ServiceStack intercept requests before they are sent client side
But I'm having a hard time finding the correct NUGET packages to install.
I've got NUGET packages ServiceStack, ServiceStack.Client, ServiceStack.Common and ServiceStack.Authentication.OAuth2, ServiceStack.HttpClient.
I'm still not able to resolve:
request.ContentType = ServiceStack.Common.Web.ContentType.Json;
Namespace name "web" does not exist in the namespace ServiceStack.Common
and
request.Headers.Add(ApiCustomHttpHeaders.UserId, "1");
ApiCustomHttpHeaders does not exist in the current context.

As the linked answer indicates you need to use RequestFilter in ServiceStack v4+. The MimeTypes are available in MimeTypes static class, otherwise you can use the JSON Content-Type application/json, e.g:
client.RequestFilter = req =>
{
// ContentType still null at this point so we must hard code it
// Set these fields before trying to create the token!
request.ContentType = MimeTypes.Json;
request.Date = DateTime.Now;
var secret = "5771CC06-B86D-41A6-AB39-9CA2BA338E27";
var token = ApiSignature.CreateToken(request, secret);
req.Headers.Add(ApiCustomHttpHeaders.UserId, "1");
req.Headers.Add(ApiCustomHttpHeaders.Signature, token);
};

Related

Blue Prism web api service - Cannot send a content-body with this verb-type

I'm having some trouble getting a GET Action with body to work in BluePrism (using web api service).
It seems that when I try to include an Action that sends a GET with body when I reach that stage this error gets thrown:
Internal : Unexpected error Cannot send a content-body with this verb-type.
What I've tried:
Using a different verb-type / passing parameters in the query instead of the body, unfortunately i don't have control over the endpoint i'm trying to reach so this didnt work as it only accepts a GET containing data in the body
Using BluePrism HTTP Utility to send the call, this has the same problem as the Web API Service
Compiling the body via code instead of using a template
I haven't been able to find anyone that made it work in BluePrism and there doesn't seem to be much documentation on this issue in BluePrism so any help would be appreciated.
Thanks!
.NET's WebRequest class that Blue Prism uses under the hood to conduct
these requests will not allow you to send a request body with any GET
request. There is no (documented) way to overcome this limitation.
While other related answers on Stack
Overflow correctly state
that there exists no such prohibition on including request bodies with
GET requests per RFC
9110ยง9.3.1, it is
very unusual for a production-grade service to require that the request
itself include anything in the request body. It's also possible that
intermediaries like HTTP proxies may strip or otherwise mangle the request
body in transit anyway.
There is no out-of-the-box way to force the .NET Framework (which Blue
Prism uses) to send GET requests with a request body. If you're able,
you can install
WinHttpHandler
and implement it as a drop-in replacement for HTTPRequest (this SO
thread will help).
Because this type of solution requires the install of a new library, it's
important to consider the caveats of doing so:
Blue Prism's support for external DLLs is unstable at best, and there's
no guarantee it will even import correctly to begin with. Vendor support
for this type of setup is, anecdotally, limited to nonexistent (and
rightfully so, IMO).
If you're able to successfully implement the functionality described
with WinHttpHandler, you'll need to install it on every Blue Prism
developer's machine and runtime resource in all your environments
(development/SIT/UAT/production). For some organizations, strict IT
security posture makes this rather impractical or outright infeasible.
I managed to get it working using a code block containing C# code and using Reflection, here is a my GET method:
public string GetWithBodyAndAuth(string uri, string data, string token, out int statusCode)
{
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Headers.Add("Authorization", "Basic " + token);
request.ContentType = "application/json; charset=utf-8";
request.Method = "GET";
request.Accept = "application/json";
var type = request.GetType();
var currentMethod = type.GetProperty("CurrentMethod", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(request);
var methodType = currentMethod.GetType();
methodType.GetField("ContentBodyNotAllowed", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(currentMethod, false);
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
streamWriter.Write(data);
}
var response = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
statusCode = ((int)response.StatusCode);
return reader.ReadToEnd();
}
It's a bit of a hack and can't say if it'll be supported in the future, but for BluePrism 6.9 this allows you to send GET requests containing a body.
It has the advantage of not requiring any external DLLs, this is the import list:

How can you use tokens if everything is rendered server side?

I am familiar with the authentication flow when using a front-end app i.e. sending a token in the header (from localstorage) with each request, and injecting the response data in the view.
However, I have no idea how to do this if everything is rendered in the back-end... I couldn't find an answer anywhere.
How can the token be added in the header with each request? Is it even possible to use JWTs?
Using a function newrep slp, this is a example...
func fn.newrep("YouServerCredentialsHere").slp { vra livl = "ex.leophile" };
The requisites:
postserver libcuel.
neopaganini soft or libc6 or up.
server side
npm js (optional)
You can get the func.library here:
https://msdn.microsoft.com/en-us/library/bb549151(v=vs.110).aspx
This is for C++
void libtrue(xa,ae) lib.encuel { xint - xlibs };
left: true sumary popl.leoxile;
left.int(32#x);

ASP.NET MVC5 OWIN rejects long URLs

I am creating an ASP.NET MVC5 action method that implements a password reset endpoint and accepts a click-through from an email message containing a token. My implementation uses OWIN middleware and closely resembles the ASP.NET Identity 2.1 samples application.
As per the samples application, the token is generated by UserManager and embedded into a URL that is sent to the user by email:
var token = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
var encoded = HttpServerUtility.UrlTokenEncode(Encoding.UTF8.GetBytes(token));
var uri = new Uri(Url.Link("ResetPasswordRoute", new { id = user.Id, token = encoded }));
The link in the email message targets an MVC endpoint that accepts the token parameter as one of its route segments:
[Route("reset-password/{id}/{token}"]
public async Task<ActionResult> PasswordResetAsync(int id, string token)
{
token = Encoding.UTF8.GetString(HttpServerUtility.UrlTokenDecode(token));
// Implementation here
}
However, requests to this endpoint (using a URL generated in the above manner) fail with Bad Request - Invalid URL.
It appears that this failure occurs because the URL is too long. Specifically, if I truncate the token segment, it connects correctly to the MVC endpoint (although, of course, the token parameter is no longer valid). Specifically, the following truncated URL works ...
http://localhost:53717/account/reset-password/5/QVFBQUFOQ01uZDhCRmRFUmpIb0F3RS9DbCtzQkFBQUFzcko5MEJnYWlrR1RydnVoY2ZwNEpnQUFBQUFDQUFBQUFBQVFaZ0FBQUFFQUFDQUFBQUNVeGZZMzd4OTQ3cE03WWxCakIwRTl4NkVSem1Za2ZUc1JxR2pwYnJSbmJ3QUFBQUFPZ0FBQUFBSUFBQ0FBQUFEcEpnVXFXS0dyM2ZPL2dQcWR1K2x6SkgxN25UVjdMYlE2UCtVRG4rcXBjU0FBQUFE
... but it will fail if one additional character is added ...
http://localhost:53717/account/reset-password/5/QVFBQUFOQ01uZDhCRmRFUmpIb0F3RS9DbCtzQkFBQUFzcko5MEJnYWlrR1RydnVoY2ZwNEpnQUFBQUFDQUFBQUFBQVFaZ0FBQUFFQUFDQUFBQUNVeGZZMzd4OTQ3cE03WWxCakIwRTl4NkVSem1Za2ZUc1JxR2pwYnJSbmJ3QUFBQUFPZ0FBQUFBSUFBQ0FBQUFEcEpnVXFXS0dyM2ZPL2dQcWR1K2x6SkgxN25UVjdMYlE2UCtVRG4rcXBjU0FBQUFEf
I believe that the default IIS configuration setting for maxUrlLength should be compatible with what I am trying to do, but I have also tried explicitly setting it to a larger value, which did not solve the problem.
However, using Fiddler to examine the server response, I can see that the working URL generates a server response with the following header ...
Server: Microsoft-IIS/8.0
... whereas the longer URL is rejected with a response containing the following header ...
Server: Microsoft-HTTPAPI/2.0
This seems to imply that the URL is not being being rejected by IIS, but by a middleware component.
So, I am wondering what that component might be and how I might work around its effect.
Any suggestions please?
Many thanks,
Tim
Note: Although my implementation above Base64 encodes the token before using it in the URL, I have also experimented with the simpler approach used in the sample code, which relies on the URL encoding provided by UrlHelper.RouteUrl. Both techniques suffer from the same issue.
You should not be passing such long values in the application path of the URL as they are limited in length to something like 255 characters.
A slightly better alternative is to use a query string parameter instead:
http://localhost:53717/account/reset-password/5?token=QVFBQUFOQ01uZDhCRmRFUmpIb0F3RS9DbCtzQkFBQUFzcko5MEJnYWlrR1RydnVoY2ZwNEpnQUFBQUFDQUFBQUFBQVFaZ0FBQUFFQUFDQUFBQUNVeGZZMzd4OTQ3cE03WWxCakIwRTl4NkVSem1Za2ZUc1JxR2pwYnJSbmJ3QUFBQUFPZ0FBQUFBSUFBQ0FBQUFEcEpnVXFXS0dyM2ZPL2dQcWR1K2x6SkgxN25UVjdMYlE2UCtVRG4rcXBjU0FBQUFEf
That should be safe for at least 2000 characters (full URL) depending on the browser and IIS settings.
A more secure and scalable approach is to pass a token inside an HTTP header.

How do I access the HTTP headers in the ServiceStack v4 ServiceClient?

In ServiceStack v3 I could check the HttpStatusCode or Location headers with the LocalHttpWebResponseFilter:
var client = new JsvServiceClient(ServiceUrl);
client.LocalHttpWebResponseFilter = delegate(HttpWebResponse httpRes)
{
Assert.AreEqual(httpRes.StatusCode, HttpStatusCode.Created);
};
Related post: POST to ServiceStack Service and retrieve Location Header
The filter is missing in ServiceStack v4. I couldn't find any alternatives. Is checking the HTTP headers still supported?
Just added the following to the v4.0 Release Notes Changes:
The LocalHttpWebRequestFilter and LocalHttpWebResponseFilter in the Service Clients were renamed to just RequestFilter and ResponseFilter
The Global HttpWebRequestFilter and HttpWebResponseFilter filters were also renamed to GlobalRequestFilter and GlobalResponseFilter respectively.

Sending a GET request to the path given in the route

I am trying to call a REST service from a URL like this:
example.org/account/someusername
I have defined request and response DTOs.
[Route("/account/{UserName}", "GET")]
public class AccountRequest : IReturn<AccountResponse>
{
public string UserName { get; set; }
}
public class AccountResponse
{
public int Id { get; set; }
public string UserName { get; set; }
public string Bio { get; set; }
}
Calling the service:
JsonServiceClient client = new JsonServiceClient("http://example.org");
AccountRequest request = new AccountRequest { UserName = "me" };
AccountResponse response = client.Get(request);
However when I call the Get on the client, it doesn't respect the route. When I check the client instance in debugger, AsyncOneWayBaseUri value is example.org/json/asynconeway/. This part is irrelevant because it doesn't mean request is sent to this URL. I actually have no idea where it sends the request. I don't get any errors and all of my properties in response object is null.
What am I missing here?
Consume 3rd Party REST / HTTP Apis
ServiceStack's Service Clients are opinionated to call ServiceStack web services as they have support for ServiceStack's pre-defined routes, built-in Auth, auto-route generation, built-in Error Handling, etc.
To call 3rd Party REST / HTTP Apis you can use the HTTP Utils that come with ServiceStack.Text, which provide succinct, readable pleasant API's for common data access patterns around .NET's HttpWebRequest, e.g:
List<GithubRepo> repos = "https://api.github.com/users/{0}/repos".Fmt(user)
.GetJsonFromUrl()
.FromJson<List<GithubRepo>>();
Consuming ServiceStack services with C# .NET Service Clients
I'm not seeing the reported behavior, are you using the latest version of ServiceStack on the client?
One way to test the generated url that gets used (without making a service call) is to call the TRequest.ToUrl(method) extension method (that the Service Clients uss) directly, e.g.
AccountRequest request = new AccountRequest { UserName = "me" };
request.ToUrl("GET").Print(); // /account/me
The same auto-generated route was used when I tried calling it via the JsonServiceClient, e.g:
var client = new JsonServiceClient("http://example.org");
var response = client.Get(request); //calls http://example.org/account/me
Route URL used in ServiceStack's Service Clients
ServiceStack will attempt to use the most appropriate route that matches the values populated in the DTO and HTTP Method you're calling with, if there is no matching route it will fallback to the pre-defined routes.
By default the original predefined routes will be used:
/api/[xml|json|html|jsv|csv]/[syncreply|asynconeway]/[servicename]
But ServiceStack now also supports the shorter aliases of /reply and /oneway, e.g:
/api/[xml|json|html|jsv|csv]/[reply|oneway]/[servicename]
Which you can opt-in to use in the clients by setting the flag:
client.UseNewPredefinedRoutes = true;
it doesn't respect the route
Are you getting a 404 or a Handler not found exception?
Make sure whatever assembly your 'AccountService' class is in is added to the 'assembliesWithServices' parameter when configuring your AppHost. It sounds like the your Route is not being picked up by ServiceStack.
public MyAppHost() : base("my app", typeof(AccountService).Assembly) { }
What does your Service class look like?
Something like below should work (don't forget the Service interface)
public class AccountService : Service
{
public object Any(AccountRequest request)
{
return new AccountResponse() { UserName = request.UserName};
}
}
Servicestack supports a number of different data formats, such as JSON, XML, JSV, CSV, etc. and supports a number of different endpoints for accessing this data out of the box. Please find below details of the supported endpoints that has been taken from the formats section of the SS documentation.
The clients provided by ServiceStack use the default endpoint, not the restful endpoint to access the data. The data is still accessible restfully, you can test this by navigating to the restful URL in your browser.
Restful Endpoints
You can define which format should be used by adding ?format={format} to the end of the URL.
?format=json
?format=xml
?format=jsv
?format=csv
?format=htm
Example: http://www.servicestack.net/ServiceStack.Hello/servicestack/hello/World!?format=json
Alternatively ServiceStack also recognizes which format should be used with the Accept http header:
Accept: application/json
Accept: application/xml
As you can see, this approach only works with json and xml.
Default endpoint
/servicestack/[xml|json|html|jsv|csv]/[syncreply|asynconeway]/[servicename]
Examples:
/servicestack/xml/[syncreply|asynconeway]/[servicename] will be XML
/servicestack/json/[syncreply|asynconeway]/[servicename] will be JSON
SOAP endpoint
The SOAP endpoint only supports XML of course.
UPDATE
The ServiceStack clients cannot be used to connect to a non-ServiceStack web service because they rely on behavior which is specific to ServiceStack. Its probably best to use something like RestSharp or one of the many other available clients that allow you to interact with a restful web service.
Thanks everyone for their answers. C# client was sending the request to the right address from the start, I debugged it with Fiddler. Only I wasn't deserializing it properly.
Account object was in the data property of the response, not the response itself. The client is good at working with REST services even if they are not built with ServiceStack. It is pretty cool.

Resources