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.
Related
I am using the Java SAP Cloud SDK version 3.11.0 and have the following VDM request:
final Destination destination = DestinationAccessor.getDestination("MyDestination");
Try<OutbDeliveryHeader> deliveryHeaderTry = Try.of(() -> new DefaultOutboundDeliveryV2Service()
.getOutbDeliveryHeaderByKey(deliveryDocument)
.execute(destination.asHttp()))
.onFailure(e -> logger.error("Failed to read delivery header " + deliveryDocument
+ ": " + e.getMessage(), e));
I need this request to be executed against the system configured in "MyDestination" with a specific SAP client. I therefore added the additional property sap-client with the corresponding value in my destination.
Unfortunately however, this request returns the following error:
Unable to fetch the metadata : Failed to execute OData Metadata request.
While debugging the SDK, I found that the method getEdm of com.sap.cloud.sdk.odatav2.connectivity.cache.metadata.GuavaMetadataCache never adds the sap-client information as either a HTTP header or URL parameter to the metadata request.(Using Postman, I was able to show that the metadata request indeed needs a sap-client, otherwise it fails. This is the explanation why the VDM request fails in the first place.)
Now my question is whether this is intended behavior or a bug in the SDK?
I figured that using .withHeader("sap-client","600").onRequestAndImplicitRequests() in my VDM request fixes my problem, but if I am supposed to add this information to every VDM request, then why should I set the sap-client in the destination?
Or is an OData metadata request designed to be "client agnostic" and that is the reason why the sap-client is not added to the metadata request in the SDK?
Since you are connecting to an S/4 OData service, the request expects additional HTTP headers. Custom values for sap-client and sap-locale can be either set manually (as you did, described in your question). Or you can use the following code:
HttpDestination destination =
DestinationAccessor.getDestination("MyDestination").asHttp()
.decorate(DefaultErpHttpDestination::new);
[...]
new DefaultOutboundDeliveryV2Service()
.getOutbDeliveryHeaderByKey(deliveryDocument)
.execute(destination));
By using this additional "decoration" step for type HttpDestination, the destination object is automatically provided the S/4 flavored properties as described above. For example, the value for sap-client saved in your destination service will be added by default, without needing to manually invoke .withHeader(...).onRequestAndImplicitRequests().
I described the context in the following SO response: https://stackoverflow.com/a/59969716/9350514
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);
};
So I'm using azure mobile services backend to try and make a custom API. However I can't seem to connect to even the template table from the client. When you make a new Azure Mobile Service using the template they provide you with this values API controller that resembles this format
[MobileAppController]
public class ValuesController : ApiController
{
// GET api/values
[Route("api/values")]
public string Get()
{
return "test";
}
}
From the client I'm trying to invoke this endpoint like this
var result = mobileService.InvokeApiAsync<string>("values", HttpMethod.Get, null).Result;
And for some reason I keep getting this exception
{"The request could not be completed. (Bad Request)"}
{Method: GET, RequestUri: 'http://localhost:58457/api/values', Version: 1.1, Content: <null>, Headers:
{
X-ZUMO-FEATURES: AT
X-ZUMO-INSTALLATION-ID: b04f4e19-4f41-46ed-9767-9c1352037559
Accept: application/json
User-Agent: ZUMO/1.3
User-Agent: (lang=Managed; os=Windows; os_version=6.1.65536.7601; arch=Win32NT; version=1.3.30324.0)
X-ZUMO-VERSION: ZUMO/1.3 (lang=Managed; os=Windows; os_version=6.1.65536.7601; arch=Win32NT; version=1.3.30324.0)
}}
This is only the template too, so I need this to work before I get any of my custom endpoints up and running. Any ideas on what the issue may be?
You can opt out of version checking by setting a value of true for the app setting MS_SkipVersionCheck. Specify this either in your web.config or in the Application Settings section of the Azure Portal.
ms_skipversioncheck to true in the portal.
You say Mobile Service, but the controller you're using is MobileAppController.
This indicates you're actually using Mobile App. If you look in your server project packages.config, you may see something like this.
<package id="Microsoft.Azure.Mobile.Server" version="1.0.119.0" targetFramework="net45" />
I suspect that the 400 you are getting is because you're using a Mobile Client version less than 2.0.0.
In your client project package config, try using a newer client version, such as:
<package id="Microsoft.Azure.Mobile.Client" version="2.0.1" targetFramework="win81" />
You should also inspect the body of the 400 response to get an explicit error message. I expect it to say something like:
{"message":"No API version was specified in the request, this request needs to specify a ZUMO-API-VERSION of '2.0.0'. For more information and supported clients see: http://go.microsoft.com/fwlink/?LinkId=690568#2.0.0"}
I have gone through the below link
https://learn.microsoft.com/en-us/azure/app-service-mobile/app-service-mobile-client-and-server-versioning
which actually says both Mobile Apps client and server SDKs are originally based on those in Mobile Services, but they are not compatible with each other. That is, you must use a Mobile Apps client SDK with a Mobile Apps server SDK and similarly for Mobile Services. This contract is enforced through a special header value used by the client and server SDKs, ZUMO-API-VERSION.
So, you must add Headers in the request
HEADERS: ZUMO-API-VERSION: 2.0.0
Or
http://localhost/api/values/get?ZUMO-API-VERSION=2.0.0
Or
You can opt out of version checking by setting a value of true for the app setting MS_SkipVersionCheck, specify this in your web.config under
Actually even if you specify what version of client you are using in your package.config you will still get the same error of Bad Request. No Zumo version specified. You must pass into your InvokeApiAsync method a parameter specifying the version. For example:
var arguments = new Dictionary<string, string>
{
{"ZUMO-API-VERSION", "2.0.0" }
};
var result = MobileService.InvokeApiAsync<string>("CONTROLLERSNAME", "HttpMethod.Get", arguements).Result;
and bingo it will work.
In my previous project, I use a framework (Agatha RRSL) similar to ServiceStack, in that everything is made of Request, Response and Handler. It also has Interceptors that can attach to handler and you can inject other interfaces to the handler as well. I can use this to open a transaction BeforeHandling, access to both request and response in AfterHandling, create audit, save to database and close transaction if needed.
I try to experiment similar with SerivceStack. But seems with Filters, I can't grab request and response together?
With custom ServiceRunner. When I try to debug OnAfterExecute(...), I can see the name of my request dto in IRequestContext {ServiceStack.ServiceHost.HttpRequestContext}. But just the name, I couldn't figure out how to retrieve the actual request object to work with the response object.
Another thing I haven't figure out is if it's possible to inject the auto wired service interface into it, like a db context or audit service. Maybe this one is too far ahead in the pipeline?
The final thing is, it seems I can only register one custom service runner? With Interceptor, I can drop a bunch of them, and they will wrap around each other.
Any thoughts? Thanks
The RequestContext also contains the HttpRequest and HttpResponse which you can get access with:
var httpReq = RequestContext.Get<IHttpRequest>();
var httpRes = RequestContext.Get<IHttpResponse>();
See the docs on Accessing HTTP Specific features for more info.
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.