ServiceStack Message via RabbitMq routing to verb other than POST - servicestack

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.

Related

How to setup ContentType for Azure ServiceBus from Spring JMS

I'm trying to use library azure-servicebus-jms-spring-boot-starter to send messages to topic. Everything works, however messages are being stored in subscriptions as application/xml type and I can't find the way how to setup this correctly to have them stored as application/json.
I've tried to configure message converter to send ContentType as described here but that doesn't work either.
#Bean
public MessageConverter jacksonJmsMessageConverter() {
final MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(){
#Override
protected TextMessage mapToTextMessage(Object object, Session session, ObjectWriter objectWriter)
throws JMSException, IOException {
final TextMessage message = super.mapToTextMessage(object, session, objectWriter);
message.setStringProperty("ContentType", "application/json");
return message;
}
};
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
converter.setObjectMapper(objectMapper);
return converter;
}
There is no exposed means of setting the content-type on the messages sent from the Qpid JMS client. The client itself uses this field as part of the JMS mapping to AMQP to distinguish certain message types that it sends and to determine at receive time what certain messages should be presented as.
It is technically possible to use reflection to reach in and so the value but the APIs you have to use from the JmsMessageFacade class are not public and could change with any release so choosing to do so comes with significant risk.

Call POX web service from Spring Integration

I'm using Spring Integration in a project that integrates (successfully) various ReST/JSON and SOAP endpoints.
Now I need to call a BusinessWorks instance that is configured to accept Plain-Old-Xml-over-HTTP.
From the "Spring Integration in Action book", I got a hint that I should use int-ws:outbound-gateway for this.
This configuration generates the correct request, but in SOAP:
<int-ws:outbound-gateway
uri="..."
request-channel="request" reply-channel="reply"
marshaller="marshaller" unmarshaller="unmarshaller"/>
I can't figure out how to configure this to send the object in the payload as POX (no SOAP envelope).
I tried this:
<int-ws:outbound-gateway
uri="..."
request-channel="request" reply-channel="reply"
marshaller="marshaller" unmarshaller="unmarshaller"
message-factory="poxMessageFactory"/>
<bean id="poxMessageFactory"
class="org.springframework.ws.pox.dom.DomPoxMessageFactory"/>
The request seems to switch correctly to XML only but the body of the request is empty (no trace of the object present in the Spring Integration payload).
Can someone tell me what I am doing wrong or how to achieve what I am trying to do?
I think this is an omission in the AbstractWebServiceOutboundGateway:
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
Object payload = this.requestMessage.getPayload();
if (message instanceof SoapMessage) {
this.doWithMessageInternal(message, payload);
AbstractWebServiceOutboundGateway.this.headerMapper
.fromHeadersToRequest(this.requestMessage.getHeaders(), (SoapMessage) message);
if (this.requestCallback != null) {
this.requestCallback.doWithMessage(message);
}
}
}
Pay attention to the if (message instanceof SoapMessage) {.
So, indeed we miss there the fact that message can be different type.
Please, open JIRA bug on the matter.
Meanwhile as a workaround I would suggest you to use WebServiceTemplate directly instead of <int-ws:outbound-gateway> you can call it from the <service-activator> using marshalSendAndReceive() method for interaction.

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.

ServiceStack.Razor CustomHttpHandler not displaying for HttpStatusCode.BadRequest

I'm trying to get a minimal app working using ServiceStack.Razor, and I'm having trouble getting a CustomHttpHandler to work. I've followed the instructions here and here, but it's not working right.
I'm using the following code to register a custom http handler for HttpStatusCode.BadRequest:
public override void Configure(Container container)
{
this.Plugins.Add(new RazorFormat());
this.SetConfig(new EndpointHostConfig
{
CustomHttpHandlers =
{
{ HttpStatusCode.NotFound, new RazorHandler("/notfound") },
{ HttpStatusCode.BadRequest, new RazorHandler("/error") }
},
DebugMode = true
});
}
The thing is, the /notfound handler works perfectly for 404s, but no matter what I do, I can't get the /error razor file to display whenever an ArgumentNullException is thrown.
My service method looks like this:
public object Any(Hello request)
{
if (string.IsNullOrEmpty(request.Name))
{
throw new ArgumentNullException("Name");
}
return new HelloResponse { Result = "Hello " + request.Name };
}
ServiceStack returns a 400 status, which is fine, but it still displays the view I have for HelloResponse:
What am I missing? Am I misunderstanding how CustomHttpHandlers are supposed to work?
For reference, I put the project up on github.
Yeah the CustomHttpHandlers are just meant for handling un-handled system generated errors. Currently they're limited to:
NotFound (404) for un-handled requests
Forbidden (403) when a request is made to an forbidden file or resource
These are the errors happen outside of ServiceStack and so isn't able to be handled by existing ServiceStack's event hooks or user-defined custom logic, so we allow users to modify the behavior in this case via CustomHttpHandlers.
The Error Handling wiki describes how to handle errors in ServiceStack.
Though it might make sense (since it's opt-in) to allow a fallback after the exception is handled to allow it to be further handled by rendering it to a user-specified page, that you're expecting to do here.
We'll look at trying to explore something like this in the future. Feel free to add future feature requests like these to ServiceStack's issue list so we don't forget.

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