ServiceStack: Handler for request not found? - servicestack

I have three routes defined. First two work fine but the last one returns error in the subject.
Routes.Add<FeeInstructionsList>("/policies/{clientPolicyId}/feeinstructions", "GET");
Routes.Add<FeeInstructionsEdit>("/policies/{clientPolicyId}/feeinstructions/{feetype}", "GET");
Routes.Add<List<FeeInstructionEditInfo>>("/policies{clientPolicyId}/feeinstructions", "POST");
When I had third route as just "/feeinstructions", it worked fine, but when I added route as above it does not work.
FeeInstructionEditInfo does not have memeber called "clientPolicyId". Could that be a reason. If so, how do I pass it to service without requiring to modify my dto. Can it be additional parameter on the service operation like this. I don't this it is possible as ss is one dto per request, but maybe there is a way?
public List<FeeInstructionEditInfo> Post(int clientPolicyId, List<FeeInstructionEditInfo> request)
Currently this method is declared as
public List<FeeInstructionEditInfo> Post(List<FeeInstructionEditInfo> request)
This is the request being sent
> POST http://localhost:12543/api/policies/680455600/feeinstructions/
> HTTP/1.1 Content-Type: application/json Accept-Language: en-US
> Referer: http://localhost:12543/ClientBin/SR.SRUnite.ShellUI.xap
> Content-Length: 1408 Accept-Encoding: identity Accept:
> application/json User-Agent: Mozilla/5.0 (compatible; MSIE 9.0;
> Windows NT 6.1; WOW64; Trident/5.0; BOIE9;ENUS) Host: localhost:12543
> Connection: Keep-Alive Pragma: no-cache
These are my original dtos, and these have not changed
[Route("/policies/{clientPolicyId}/feeinstructions/{feetype}","GET")]
public class FeeInstructionsEdit
{
public int ClientPolicyId { get; set; }
public string FeeType { get; set; }
}
public class FeeInstructionsEditResponse
{
public List<KeyValuePair> Instructions { get; set; }
public List<KeyValuePair> Contacts { get; set; }
public List<FeeInstructionEditInfo> FeeInstructions { get; set; }
}
public partial class FeeInstructionEditInfo
{
public int FeeInstructionId { get; set; }
public string FeeTypeCode { get; set; }
public string FeeTypeDescription { get; set; }
public string FeeTypeGroupCode { get; set; }
public string ProductName { get; set; }
public string InsuredType { get; set; }
public string Demographic { get; set; }
public string Instruction { get; set; }
public string Contact { get; set; }
public decimal? FeeAmount { get; set; }
}
I would post list of FeeInstructionEditInfo to /clientPolicies/342434/feeinstructions and it would not work, but posting to /feeinstructions would.
I now post using this pair of dto's.
[Route("feeinstructions","POST")]
public class FeeInstructionsSave
{
public int ClientPolicyId { get; set; }
public List<FeeInstructionEditInfo> FeeInstructions { get; set; }
}
public class FeeInstructionsSaveResponse : IHasResponseStatus
{
public ResponseStatus ResponseStatus { get; set; }
}

A fundamental concept in ServiceStack is that every service needs to be called with a Request DTO. This Request DTO can be populated with any combination of PathInfo, QueryString and Request Body.
This means if you wanted to pass in a collection you would need to have your Request DTO inherit from it, e.g:
[Route("/policies/{clientPolicyId}/feeinstructions", "POST")]
public class EditFeeInstructions : List<FeeInstructionEditInfo>
{
}
Now everything behaves as normal:
public List<FeeInstructionEditInfo> Post(EditFeeInstructions request)
{
...
}
For an Empty Request your service would look like:
public class EmptyRequest {}
public object Post(EmptyRequest request)
{
...
}
Even if you want to process the request manually yourself, you would still need to provide a Request DTO signalling your intent, e.g:
public class Hello : IRequiresRequestStream
{
//The raw Http Request Input Stream gets injected here
public Stream RequestStream { get; set; }
}

Related

ServiceStack Wrapper for Bloomberg OpenFIGI

I need to make the following call to an open API (https://www.openfigi.com/api)
Curl Example:
curl -v -X POST 'https://api.openfigi.com/v1/mapping' \
--header 'Content-Type: text/json' \
--data '[{"idType":"ID_WERTPAPIER","idValue":"851399","exchCode":"US"}]'
Request Format
The request is passed in via HTTP request body. The only supported HTTP verb is POST. Here is a sample request to the API:
[
{"idType":"ID_ISIN","idValue":"US4592001014"},
{"idType":"ID_WERTPAPIER","idValue":"851399","exchCode":"US"},
{"idType":"ID_BB_UNIQUE","idValue":"EQ0010080100001000","currency": "USD"},
{"idType":"ID_SEDOL","idValue":"2005973","micCode":"EDGX", "currency":"USD"}
]
Using ServiceStack Request DTO, how do I make a RequestDto to achieve a call to to the above third party service endpoint.
This is just an exercise of creating DTOs which match the shape of the JSON you want to output and JSON you want to receive. To emit the exact the exact JSON property names you can either use [DataMember] on the Request DTO, or JsConfig.EmitCamelCaseNames = true to tell ServiceStack to serialize properties in camelCase or you can use JsConfig.With() to create a Custom Scope.
I've created a Live example of this in Gistlyn which you can use to experiment against Bloomberg's API.
I've used [DataMember] attribute here as it will work independent of your Json Serialization config. You don't need to do this for the Response DTO because ServiceStack Serializers is case-insensitive.
So to send the Request that matches the shape of that JSON you can use:
[DataContract]
public class Mapping
{
[DataMember(Name="idType")]
public string IdType { get; set; }
[DataMember(Name="idValue")]
public string IdValue { get; set; }
[DataMember(Name="exchCode")]
public string ExchCode { get; set; }
[DataMember(Name="currency")]
public string Currency { get; set; }
[DataMember(Name="micCode")]
public string MicCode { get; set; }
}
You can use ServiceStack's HTTP Utils to easily send requests to 3rd Party APIs, e.g:
var url = "https://api.openfigi.com/v1/mapping";
var json = url.PostJsonToUrl(new[]{
new Mapping { IdType = "ID_ISIN", IdValue = "US4592001014" },
new Mapping { IdType = "ID_WERTPAPIER", IdValue = "851399", ExchCode = "US" },
new Mapping { IdType = "ID_BB_UNIQUE", IdValue = "EQ0010080100001000", Currency = "USD" },
new Mapping { IdType = "ID_SEDOL", IdValue = "2005973", MicCode = "EDGX", Currency = "USD" },
});
Then to receive the response you need to create DTOs which match the shape of the JSON Response which looks like:
public class BloombertResult
{
public string Figi { get; set; }
public string SecurityType { get; set; }
public string MarketSector { get; set; }
public string Ticker { get; set; }
public string Name { get; set; }
public string UniqueId { get; set; }
public string ExchCode { get; set; }
public string ShareClassFIGI { get; set; }
public string CompositeFIGI { get; set; }
public string SecurityType2 { get; set; }
public string SecurityDescription { get; set; }
public string UniqueIdFutOpt { get; set; }
}
public class BloombergResponse
{
public List<BloombertResult> Data { get; set; }
public string Error { get; set; }
}
Which you can just deserialize into a collection of BloombergResponse, e.g:
var response = json.FromJson<BloombergResponse[]>();
Gistlyn will show you a nice human readable preview of each variable by clicking on it in the watch window. Or if you're this in a C# Unit test you can quickly see to populated DTOs with:
response.PrintDump();

.NET core model binding with hyphenated attribute names in post request

I've subscribed to the Nexmo SMS service and they offer a callback URL for inbound SMS. The post request gives the following Json structure when notifying of SMS receipt:
{
"msisdn": "441632960960",
"to": "441632960961",
"messageId": "02000000E68951D8",
"text": "Hello7",
"type": "text",
"keyword": "HELLO7",
"message-timestamp": "2016-07-05 21:46:15"
}
Using the following code snippet, I can map all of the fields to my SmsReceipt apart from 'message-timestamp'. None of the message timestamp fields are populated.
public class SmsReceipt
{
public string msisdn { get; set; }
public string to { get; set; }
public string messageId { get; set; }
public string text { get; set; }
public string type { get; set; }
public string keyword { get; set; }
public string messagetimestamp { get; set; }
public string messageTimestamp { get; set; }
public string message_timestamp { get; set; }
}
[HttpPost("inboundsms")]
public async Task<IActionResult> Post([FromBody] SmsReceipt receipt)
{
return StatusCode(200);
}
I guess the same applies to incoming requests with other special characters such as '.' Any ideas greatly appreciated.
Your property name should match with the property name in the data being sent. Looks like your payload property name is message-timestamp. You cannot create a C# property with a - in it. So your options are
Either update your json payload property to match with one from your C# class.
Decorate your C# class with JsonProperty(From Newtonsoft.Json) where you specify what property from the posted data should be mapped to this property.
Also i suggest use the DateTime type. That type was created to deal with date time value.
public class SmsReceipt
{
public string Msisdn { get; set; }
public string To { get; set; }
public string MessageId { get; set; }
public string Text { get; set; }
public string Type { get; set; }
public string Keyword { get; set; }
[JsonProperty("message-timestamp")]
public DateTime Messagetimestamp { get; set; }
}

can the parameters FromUri be read from an attribute?

I have a simple controller that accepts a response from a payment system.
public async Task<IHttpActionResult> Pending([FromUri] DepositResponse response)
{
Logger.LogInfo(JsonConvert.SerializeObject(response));
return Ok(response);
}
the deposit response however has very ugly and unstandardised parameters. I have no control over that because that's what the payment system sends.
public class DepositResponse
{
public string ppp_status { get; set; }
public string ExErrorCode { get; set; }
public string PPP_TransactionID { get; set; }
public string merchant_site_id { get; set; }
//etc
}
As a result, Resharper complains about the chosen name that it doesn't match the rules and I want to change it to match all the classes in the rest of the project.
Is there an attribute I can use, or one I can create to make FromUri understand the response?
For example
public class DepositResponse
{
[FromUriName("ppp_status")]
public string pppStatus { get; set; }
[FromUriName("ExErrorCode")]
public string exErrorCode { get; set; }
[FromUriName("PPP_TransactionID")]
public string pppTransactionId { get; set; }
[FromUriName("merchant_site_id")]
public string merchantSiteId { get; set; }
//etc
}
I couldn't find such an example online, but I would imagine it can be very useful when dealing with external systems that send rubbish...
any ideas?
You can use the below model
public class DepositResponse
{
[JsonProperty(PropertyName="ppp_status")]
public string pppStatus { get; set; }
[JsonProperty(PropertyName="ExErrorCode")]
public string exErrorCode { get; set; }
[JsonProperty(PropertyName="PPP_TransactionID")]
public string pppTransactionId { get; set; }
[PropertyName(PropertyName="merchant_site_id")]
public string merchantSiteId { get; set; }
//etc
}

ServiceStack: Errors not serialized to responsestatus

Iam new to service stack and have been strugling for hours, trying to make servicestak work for me. For now the major show stopper is that i cann't make the exception part work. I registered all plugins by the book and services work for both REST, Soap, CSV, XML and JSV. The project contains 4 basic test methods for crud operations on a customer object. When an error is thrown i do not get the expected error: ResponseStatus is not set and a generel error is generated. Can some one please help me find out why?
https://dl.dropbox.com/u/101619220/TestingServiceStack.zip
EDIT: Thanks for comment :)
I created a simple AppHost file:
namespace TestingServiceStack
{
public class AppHost : AppHostBase
{
public AppHost()
: base("StarterTemplate ASP.NET Host", typeof(CustomersService).Assembly)
{
}
public override void Configure(Container container)
{
Plugins.Add(new ValidationFeature());
Plugins.Add(new RequestLogsFeature());
SetConfig(new EndpointHostConfig
{
DebugMode = true, //Enable StackTraces in development
});
LogManager.LogFactory = new Log4NetFactory(true);
JsConfig.EmitCamelCaseNames = true;
JsConfig.DateHandler = JsonDateHandler.ISO8601;
Routes.Add<GetCustomers>("/customers", "GET")
.Add<GetCustomers>("/customers/{Id}", "GET")
.Add<AddCustomer>("/customers", "POST")
.Add<UpdateCustomer>("/customers/{Id}", "PUT")
.Add<DeleteCustomer>("/customers/{Id}", "DELETE");
}
public static void Start()
{
new AppHost().Init();
}
}
}
And a service:
namespace TestingServiceStack
{
public class CustomersService : Service
{
#region Logging
private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
#endregion
public object Any(GetCustomers request)
{
GetCustomersResponse response = null;
try
{
if (request.Id != "0")
throw HttpError.NotFound("Id {0} throws error".Fmt(request.Id));
response = new GetCustomersResponse {Id = request.Id ?? "notset", Name = "GetCustomers"};
}
catch (Exception ex)
{
Log.Error(base.RequestContext.Get<IHttpRequest>(), ex);
throw;
}
return response;
}
public object Any(AddCustomer request)
{
return new AddCustomerResponse {Id = request.Id, Name = "AddCustomer"};
}
public object Any(UpdateCustomer request)
{
return new UpdateCustomerResponse {Id = request.Id, Name = request.Name};
}
public object Any(DeleteCustomer request)
{
return new DeleteCustomerResponse {Id = request.Id, Name = "DeleteCustomer"};
}
}
}
And the exchanged objects are:
using System.Runtime.Serialization;
using ServiceStack.ServiceInterface.ServiceModel;
namespace TestingServiceStack
{
[DataContract]
public class GetCustomers
{
[DataMember]
public string Id { get; set; }
}
[DataContract]
public class UpdateCustomer
{
[DataMember]
public string Id { get; set; }
[DataMember]
public string Name { get; set; }
}
[DataContract]
public class AddCustomer
{
[DataMember]
public string Id { get; set; }
[DataMember]
public string Name { get; set; }
}
[DataContract]
public class DeleteCustomer
{
[DataMember]
public string Id { get; set; }
}
[DataContract]
public class GetCustomersResponse : IHasResponseStatus
{
[DataMember]
public string Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public ResponseStatus ResponseStatus { get; set; }
}
[DataContract]
public class UpdateCustomerResponse : IHasResponseStatus
{
[DataMember]
public string Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public ResponseStatus ResponseStatus { get; set; }
}
[DataContract]
public class AddCustomerResponse : IHasResponseStatus
{
[DataMember]
public string Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public ResponseStatus ResponseStatus { get; set; }
}
[DataContract]
public class DeleteCustomerResponse : IHasResponseStatus
{
[DataMember]
public string Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public ResponseStatus ResponseStatus { get; set; }
}
}
I use SoapUi to call the method GetCustomers that throws an error if id equals 0, and i would expect the ResponseStatus to be set, but it isn't. When calling from SoapUi i get the following error:
I have no clue how to get reponsestatus set proberly, any hints are appreciated.
I registered all plugins by the book and services work for both REST, Soap, CSV, XML and JSV
To echo #mythz it's much easier to answer direct questions with clearly stated problems with examples of errors or exceptions. My issue with statements/generalizations like above is that I don't know what 'by the book' means nor do I know your concept of working is (could be build succeeds, metadata page is displayed, etc)
ResponseStatus is not set and a generel error is generated.
In your CustomersService class it looks you are throwing an error (HttpError) and catching/logging it. The code will then proceed to return a null response. ServiceStack has native support for throwing of exceptions. If you add a throw into your catch (assuming you want to keep the catch for logging purposes) you should get a populated ResponseStatus.
GetCustomersResponse response = null;
try
{
if (request.Id != "0")
throw HttpError.NotFound("Id {0} throws error".Fmt(request.Id));
response = new GetCustomersResponse {Id = request.Id ?? "notset", Name = "GetCustomers"};
}
catch (Exception ex)
{
Log.Error(base.RequestContext.Get<IHttpRequest>(), ex);
throw; //Let ServiceStack do its thing.
}
return response;
SoapUI
This change may fix the issue with soapUI but I'm unclear as what 'general error' you are receiving. I'm guessing the issue could be due to 'deserializing' a null response.
ServiceStack doesn't support troubleshooting with 3rd party SOAP libraries or client proxies.
See WebServicesTests.cs for examples of exceptions in integration tests. For SOAP you also want to read up on ServiceStack's SOAP Support and its limitations
We had an issue where ResponseStatus wasn't being populated - we'd decorated the Response DTO with DataContract/DataMember, but not the ResponseStatus property. Once we added that decoration, all was joyful.

ServiceStack not receiving values on OnDelete

On OnDelete of ServiceStack, it is called but the values are empty.
I tried to check the value, e.g.
ProductRequestResponse rx = Client.Send<ProductRequestResponse>(
"DELETE", "http://localhost:2012/api/product_request",
new ProductRequest { Id = 7 });
On the ServiceStack side, I only receive an Id of 0. Here's my StackService OnDelete method.
public override object OnDelete(ProductRequest request)
{
throw new Exception("Id: " + request.Id.ToString());
}
Here's my objects use for communication
public class ProductRequest
{
public int Id { get; set; }
public ProductDto ProductDto { get; set; }
}
public class ProductRequestResponse
{
public ProductDto ProductDto { get; set; }
public IEnumerable<ProductDto> ProductDtos { get; set; }
public ServiceStack.ServiceInterface.ServiceModel.ResponseStatus ResponseStatus { get; set; } //Where Exceptions get auto-serialized
}
What am I missing, why StackService is not receiving any value from OnDelete method?
Firstly, you should be using the Delete method as the Send only does POST's:
So it looks something like:
restClient.Delete<TransactionLogResponse>("/transactionlog");
The reason why Delete doesn't expect a Request DTO is because the DELETE Http verb does not accept a request body.
If you want to add paramaters you should add this on the route path or query string, e.g:
restClient.Delete<TransactionLogResponse>("/transactionlog/1?Arg1=a&Arg2=b");

Resources