Sending a GET request to the path given in the route - servicestack

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.

Related

REST API url convension for filtering resources with different criterias

I have a REST API to expose a resource, Employee with the following fields(id, firstName, LastName, Age, Salary). (Please note that this is a sample resource and my actual resource is more complex) This is an ASP.Net WEB API which serves to an Angular front end.
Few of my current REST API endpoints are as follows
HTTP GET (Get all the employees) api/employees
HTTP GET (Get a single employee by id) api/employees/{id}
PUT , POST and DELETE are following the normal REST standard
Now I have few different filtering requirements like Get all emloyees by FirstName, Get all Employees by Last Name, Get all employees who's salary is greater than 1000
in an RPC setup I would create methods like
GetEmployeesByFirstName('donald')
GetEmployeesByLastName('trump')
GetEmployeesBySalaryGreaterThan(1000)
and achieve this.
But I am a bit confused on how to design these URLs according to the REST API standards.
I thought of doing like below but I feel these are also not conforming to the REST standard (as I understand it)
api/employees/get-by-firstname?firstName=donald OR api/employees/by-firstname?firstName=donald
api/employees/get-by-lastname?lastName=trump OR api/employees/by-lastname?lastName=trump
api/employees/get-salary-greterthan?salary=1000 OR api/employees/salary-greterthan?salary=1000
I really think the URLs should be like
api/employees?firstName=donald
api/employees?lastName=trump
api/employees?salary=1000(hmm not sure about this one)
But I am having issues creating my ASP.Net Web API controller as the route is almost the same api/employees and it gives me exceptions
The project I am working on has some rules saying that we have to follow the REST standard when creating APIs. Can someone help me on how I should design my URLs in this kind of filtering situations
If you are querying employees then the following URLs should all hit the same action method
api/employees?firstName=donald
api/employees?lastName=trump
api/employees?salary=1000
To do this you should create an object that will capture the possible parameters:
public class EmployeeFilterParams{
public string firstName { get; set; }
public string lastName { get; set; }
public string salary { get; set; }
}
and then create the action in the Employees controller:
[HttpGet]
public ActionResult Get(EmployeeFilterParams params){
if (!string.IsNullOrEmpty(params.firstName)){
// do something here for firstName
}
... repeat for each parameter
}
Because this is a GET request ASP.Net's default model binding should populate the properties in params (EmployeeFilterParams).
This method has the added benefit that you can easily filter on multiple parameters i.e.
api/employees?firstName=donald&lastName=trump&salary=1000
THIS CODE IS UNTESTED BUT SHOULD GIVE YOU A GOOD STARTING POINT
I am a bit confused on how to design these URLs according to the REST API standards.
REST doesn't care what spelling conventions you use for your URLs. As far as a consumer is concerned, they are opaque identifiers. Any information encoded into the identifier is done at the server's discretion and for its own convenience.
Which is good in that it means that you, the server, can choose identifier spellings that work with whatever local routing library you happen to be using. So you can choose any spelling that makes ASP.Net Web API Controller easy to work with, and that's fine.
/api/employees/get-salary-greterthan?salary=1000
/api/employees/salary-greterthan?salary=1000
/api/employees?salary=100
/api/reports/employees-by-salary?greaterThan=1000
/api/reports/employees-by-salary/greaterThan/1000
/api/9048aa3e-9058-4248-8949-459bb4a02019
Those are all fine.
Identifiers that use key/value pairs in the query are convenient when you are using HTML to interact with your API, because the HTML forms can be used as a sort of URI template. If you are targeting clients with more sophisticated template capabilities, then you have more freedom about how you encode the information into the URI.
I have seen in most of the documents which explains REST url naming convensions saying that we should not use VERBs in the url (like "get" in the get-salary-greterthan part of the url).
https://www.merriam-webster.com/dictionary/put
https://www.merriam-webster.com/dictionary/post
https://www.merriam-webster.com/dictionary/patch
Notice that these URI work exactly as you would expect, even though put, post, and patch are all registered HTTP method tokens.
URI spelling conventions are analogous to spelling conventions for variable names - they are there just to make things "easy" for human beings. The machines don't care.

Azure Mobile Services Custom API Getting Bad Request on Client

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.

ServiceStack Message via RabbitMq routing to verb other than POST

implementing service bus with servicestack and rabbitmq here.
Documentation states "each message will instead be executed by the best matching ServiceStack Service that handles the message with either a Post or Any fallback verb".
How then would I make the published message from the client route to PUT?
Thanks in advance for any suggestions or samples.
each message will instead be executed by the best matching ServiceStack Service that handles the message with either a Post or Any fallback verb"
This documentation says that messages are treated as POST requests, as such can only be handled with Post(Request) or Any(Request) handlers. This is the same as ServiceStack's SOAP Support where all SOAP request are POST's, which you can ensure is accessible by SOAP/MQ requests by maintaining separate Request DTO's (a common and recommended practice) and implementing them using Any() so they're still accessible by both PUT and POST requests, e.g:
[Route("/customers", "POST"]
public class CreateCustomer { ... }
[Route("/customers/{Id}", "PUT"]
public class UpdateCustomer { ... }
public class CustomerService : Service
{
public object Any(CreateCustomer request) { ... }
public object Any(UpdateCustomer request) { ... }
}
This service enables access to the service via POST /customers and PUT /customers/1 HTTP routes while still allowing them to be accessed via SOAP/MQ.

Does netsuite have REST ful API? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 2 years ago.
Improve this question
I want to know does Netsuite provides REST ful api? Currently i am doing integration with my application(java) with soap based web services.i have done some research but didn't get useful information.IF it does where can i found api?
Avoid the SuiteTalk SOAP web services API like the plague; it will do nothing but waste your time to. Usage of Netsuite SOAP API is viable only when you are okay with the SOAP API being non performant, don't mind interacting with gross buggy SOAP API, have much time to implement robust error handling to account for the random SOAP errors, concurrency errors. You'll need much time to develop robust fault tolerance. All that time will be wasted time; because no amount of time will make the SOAP API performance acceptable.
RESTlet's are preferred over SOAP API usage for writing data; RESTlets tend to be slightly more performant for writes (although responses are still extremely slow and not suitable for a customer facing app).
RESTlet's are a viable short term solution for writing data to Netsuite. Its essentially a JS script that allows you to set up a token based auth poor man's JSON endpoint; in which you can send JSON request bodies and get back JSON response bodies. Usage is reasonable in cases in which not much data needs to be written via the Restlet's (for instance for SalesOrders). A queue based system and background jobs with retry capabilities will mitigate the random Netsuite error issues (concurrency errors, timeouts etc).
If you must write to a bunch of Netsuite entities frequently and are using Netsuite as the source of truth for your data rather than attempting to build an entire REST like JSON API on top of Netsuite; I'd recommend implementing a pub/sub service in which Netsuite publishes events to an external service subscribed to by your app/API. Your app could also publish mutations to a channel subscribed to by Netsuite. This way data mutations sent to Netsuite can occur in a middle layer with reduced complexity.
To fetch Netsuite data for outside apps the most efficient means available appears to be the Netsuite ODBC database driver; it provides a direct connection to Netsuite database read only table views. Simple select queries for a set of Items that with same schema in Postgres or MySQL typically take 0.5 ms or less; typically take between 15 seconds to slightly over 100 seconds to return.
Connection timeouts and other errors from Netsuite are still common using NS ODBC driver. Despite slow query results retrieval of all data needed for a set of 5000 items in 14 seconds is far better than the hours it would take to get the same via Netsuite's SOAP API.
Yes. That is in Customization/Scripts section. You will find "RestLet" there. Doc is here.
However you said your application is soap based, I suggest you take a look Netsuite's WebServices aka SuiteTalk.
The SuiteTalk Platform provides programmatic access to your NetSuite data and business processes through an XML-based application programming interface (API).
I think you do need to access to your Netsuite data, right?
You can download their sample for test and learning.
In NetSuite, you can build RESTlet scripts which provide a REST-based interface. You can essentially use them to build your own JSON API. Recommend researching RESTlets in the NetSuite Help.
SOAP is easier to configure and use, but only allows 1 connection per
Netsuite account (you use your login credentials as authentication)
and is relatively slow.
That's not quite true, as you can extend it with suite cloud plus program. Check help for:
- Understanding Web Services Governance
- Enabling Web Services Concurrent Users with SuiteCloud Plus
UPDATE: There are two types of governance in NetSuite since approx July 2016 - user governance (also known as a legacy governance model, implicitly used when sessions / SOAP Login method are utilized) and account governance. In the account governance there is a shared pool for all incoming concurrent requests (no sessions should be used, authentication via user credentials or Token-Based Authentication).
This is the proper REST API provided by NetSuite for integration purposes.
https://system.netsuite.com/help/helpcenter/en_US/APIs/REST_API_Browser/record/v1/2020.1/index.html
The REST API can be invoked either via Token-based authentication or OAuth 2.0 enabled HTTP client.
First you need to login to NetSuite account and enable the SuiteTalk Webservice features of the account (Setup->Company->Enable Features).
Then obtain the SuiteTalk Base URL, which contains the account ID under the company URLs (Setup->Company->Company Information). E.g., https://<ACCOUNT_ID>.suitetalk.api.netsuite.com
After that create an integration application (Setup->Integration->New), enable OAuth 2.0 or TBA. This blog contains the process of enabling features and obtaining tokens.
Then use the BaseUrl + API resource path to as the HTTP client path to invoke each record API. Operations such as CRUD, search and filter can be done via this REST API. For more information See NetSuite Documentation
Yes, Netsuite supports REST web services.
Here's a working Java example, that uses the open source scribe library.
Note that an Accept (and for Posts, a Content-Type) header of application/json is needed for Netsuite to accept the requests, otherwise you'll get a "Request media type is not valid." error. Also getSignatureType method must be implemented for API class (NetSuiteApi.java).
Change all the string constants to suit your setup. Note that this code will also work with Netsuite RESTlets.
REST documentation is available here:
https://[your-netsuite-ID].app.netsuite.com/help/helpcenter/en_US/PDF/REST_Web_Services.pdf
File #1: NetSuiteApi.java
package com.scribe.api;
import com.github.scribejava.core.builder.api.DefaultApi10a;
import com.github.scribejava.core.model.OAuth1RequestToken;
public class NetSuiteApi extends DefaultApi10a {
private static class InstanceHolder {
private static final NetSuiteApi INSTANCE = new NetSuiteApi();
}
public static NetSuiteApi instance() {
return InstanceHolder.INSTANCE;
}
#Override
public String getAccessTokenEndpoint() {
return null;
}
#Override
public String getRequestTokenEndpoint() {
return null;
}
#Override
public String getAuthorizationUrl(OAuth1RequestToken requestToken) {
return null;
}
#Override
protected String getAuthorizationBaseUrl() {
return null;
}
#Override
public OAuth1SignatureType getSignatureType() {
return OAuth1SignatureType.HEADER;
}
}
File #2: NetSuiteApiCallExample.java
package com.scribe.api;
import com.github.scribejava.core.builder.ServiceBuilder;
import com.github.scribejava.core.model.OAuth1AccessToken;
import com.github.scribejava.core.model.OAuthRequest;
import com.github.scribejava.core.model.Response;
import com.github.scribejava.core.model.Verb;
import com.github.scribejava.core.oauth.OAuth10aService;
public final class NetSuiteRestExample {
private String CONSUMER_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
private String CONSUMER_SECRET = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
private String TOKEN_ID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
private String TOKEN_SECRET = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
private String REST_URL = "https://1234567-sb1.suitetalk.api.netsuite.com/rest/platform/v1/record/inventoryitem/";
private String REALM = "1234567_SB1";
private String POSTBODY = "{\"type\": \"SIMPLE\",\"authorId\": -5}";
public static void main(String[] args) {
final OAuth10aService service = new ServiceBuilder(CONSUMER_KEY).apiSecret(CONSUMER_SECRET))
.build(NetSuiteApi.instance());
OAuth1AccessToken accessToken = new OAuth1AccessToken(TOKEN_ID, TOKEN_SECRET);
// This is POST method call
// OAuthRequest request = new OAuthRequest(Verb.POST, REST_URL);
// request.addHeader("Content-Type", "application/json");
// // Without next line, you'll get a "Request media type is not valid." error, even though this is not needed with Postman
// request.addHeader("Accept", "application/json");
// request.setRealm(REALM);
// request.setPayload(POSTBODY);
// This is GET method call
OAuthRequest request = new OAuthRequest(Verb.GET, params.get("REST_URL"));
// Without next line, you'll get a "Request media type is not valid." error, even though this is not needed with Postman
request.addHeader("Accept", "application/json");
request.setRealm(params.get("REALM"));
service.signRequest(accessToken, request);
System.out.println("Sending this request...");
System.out.println(request.getHeaders());
System.out.println(request.getCompleteUrl());
// System.out.println(request.getPayload());
final Response response = service.execute(request);
System.out.println("Got this response...");
System.out.println(response.getCode() + "\n" + response.getHeaders());
System.out.println(response.getBody());
return response.getBody();
}
}
Add this to you Maven dependencies (pom.xml):
...
<dependencies>
...
<dependency>
<groupId>com.github.scribejava</groupId>
<artifactId>scribejava-apis</artifactId>
<version>6.9.0</version>
</dependency>
</dependencies>

Logging Servicestack Bad Requests

Is there a way to log the validation results which get populated in the ResponseStatus when you have the ValidationFeature plugin enabled?
From what i can understand, any requests coming in get validated and if the validation passes then it goes to the service. Using request filters i can log the requests coming in however using the response filters i can only log valid requests.
I trying to log all responses especially HttpStatus 400 (Bad request) which is returned as a result of a validation error.
I have also tried to play a bit with the RequestLog plugin but from what i understood what gets logged are only valid request (i.e requests that went to the service).
I hope you understand what i am trying to say.
Look to see how 7digital have customized ServiceStack's Validation Feature to support logging.
Related Features
The new API supports the concept of a ServiceRunner you can override to add your own event and exception hooks you can read about in the wiki.
There is also the built-in Request Logger that lets you expect details of the more recently processed requests.
Create an issue on the GitHub project if you want to see logging in the validation feature.
A quick way to log errors from the ValidationFeature plugin is simply thus:
public override void Configure(Container container)
{
...etc...
Plugins.Add(new RequestLogsFeature() { etc });
Plugins.Add(new ValidationFeature()
{
ErrorResponseFilter = MyValidationError
});
...etc...
}
public object MyValidationError(ValidationResult validationResult, object errorDto)
{
Container.Resolve<IRequestLogger>().Log(null, null, errorDto, TimeSpan.Zero);
return errorDto;
}
However, in my case, I realized that logging validation errors in the service itself is not the proper place; I instead log these errors in the applications that consume the service.

Resources