Selectively allow SOAP on one or more messages in ServiceStack - soap

We are currently using ServiceStack for our web api which is 99% REST/JSON however we have one new message that we need to allow a SOAP endpoint for. (The client is an older BizTalk server and SOAP 12 is required.) We want to prevent SOAP on everything else but this one message. Is there a way to enable the SOAP Feature on a single message or service? What are the scopes to which SOAP can be specified?
In our AppHost Configure() we have removed the Features we want to disallow with EnableFeatures = Feature.All.Remove( Feature.Csv | Feature.Html | Feature.Jsv | Feature.Soap ) however this removes our ability to expose the SOAP12 WSDL. On the flip-side, not removing Feature.Soap enables SOAP for all POST messages which we need to prevent.
Any help is appreciated.

I have found the answer to my question.
ServiceStack has provided a way to specify what endpoint channels are available and to whom using the ServiceStack.ServiceHost.RestrictAttribute (https://github.com/ServiceStack/ServiceStack/wiki/Security). In order to limit the visibility of my message to only SOAP12:
//Request DTO
[DataContract(Namespace = Namespaces.Messages.NS_2013_01 )]
[Restrict(EndpointAttributes.Soap12 )]
public class Hello
{
public string Name { get; set; }
}
The visibility in my metadata is restricted appropriately:

Related

Controller return type and httpStatus best practice and production/consumption on method in REST WS

I commence in REST and I have some questions:
What type must the controller return? Typically, I'm asking if my Rest #Controller must return Item object as it is or encapsulate it in ResponseEntity in order to specify http-status-code.
What http status code to use in a GET method on a particular item ("/items/2") if the given item does not exists: HttpMediaStatus.OK(200) and null return or HttpStatus.NO_CONTENT(204) and null return ?
Second part: I saw it was possible to specify #Produces and #Consumes on WS method but what the use of that? My application and my methods work so, why specify MediaType.APPLICATION_JSON_VALUE? Doesn't Spring/SpringBoot automatically convert Item or ResponseEntity into json?
Context: using Spring Boot, hibernate, REST webservice.
Thank you.
Many questions in one, I'll provide short answers with a bunch of link to relevant articles and the reference documentation.
What type must the controller return?
Depends on your annotation and the RESTful-ness of your service. There are three annotations you can use for controllers: #Controller, #RestController and #RepositoryRestController.
Controller is the base annotation to mark your class as a controller. The return type of the controller endpoint methods can be many things, I invite you to read this dedicated post to get a grasp of it.
When developing a pure-REST service, you will focus on using RestController and RepositoryRestController.
RestControlleris Controller + ResponseBody. It binds the return value of the endpoint method to the web response body:
#RestController
public ItemController {
#RequestMapping("/items/{id}")
public Item getItem(#PathVariable("id") String id) {
Item item = ...
return item;
}
}
With this, when you hit http:/.../api/items/foo, Spring does its magic, automatically converting the item to a ResponseEntity with a relevant 40X status code and some default HTTP headers.
At some point, you will need more control over the status code and headers, while still benefiting from Spring Data REST's settings. That's when you will use RepositoryRestController with a ResponseEntity<Item> as return type, see the example the Spring Data REST reference.
What http status code to use in a GET method on a particular item if the given item does not exists?
Bluntly said: use HttpStatus.NOT_FOUND. You're looking for a resource that does not exist, there's something wrong.
That being said, it is completely up to you to decide how to handle missing resources in your project. If your workflow justifies it, a missing resource could be something completely acceptable that indeed returns a 20X response, though you may expect users of your API to get confused if you haven't warned them or provided some documentation (we are creatures of habits and conventions). But I'd still start with a 404 status code.
(...) #Produces and #Consumes on WS method but what the use of that? My application and my methods work so, why specify MediaType.APPLICATION_JSON_VALUE? Doesn't Spring/SpringBoot automatically convert Item or ResponseEntity into json?
#Consumes and #Produces are respectively matched against content-type and accept headers from the request. It's a mean of restricting the input accepted and the output provided by your endpoint method.
Since we're talking about a REST service, communications between clients of the API and the service are expected to be JSON-formatted. Thanks to Spring HATEOAS, the answer are actually formatted with the application/hal+json content-type.
In that scenario, you can indeed not bother with those two annotations. You will need them if you develop a service that accepts different content-types (application/text, application/json, application/xml...) and provides, for instance, HTML views to users of your website and JSON or XML response to automated clients of your service.
For real life examples:
Facebook provides the Graph API for applications to read to/write from its graph, while users happily (?) surf on web pages
Google does the same with the Google Maps API

Setting Scenario Endpoints

I am having problems setting an endpoint URI in a scenario for the Citrus Simulator. Here is how I am trying to build my scenario:
#Override
public void run(ScenarioDesigner scenario) {
scenario
.soap()
.receive().endpoint("{http://www.sikorsoftware.com/lov/schemas}LOVRequest")
.payload("<ns2:LOVRequest xmlns:ns2=\"http://www.sikorsoftware.com/lov/schemas\"><ns2:id>123456</ns2:id></ns2:LOVRequest>");
scenario
.soap()
.send()
.payload("<LOVResponse xmlns=\"http://www.sikorsoftware.com/lov/schemas\">" +
"Hi there!" +
"</LOVResponse>");
}
But I keep getting this message when I try to send a soap message:
o.s.ws.server.EndpointNotFound : No endpoint mapping found
for [SaajSoapMessage
{http://www.sikorsoftware.com/lov/schemas}LOVRequest]
What am I doing wrong. Should I be setting up my endpoints a different way?
Thanks,
Michael
The endpoint is always a reference to a Citrus Spring bean component, in particular the component id that is used to add the component to the Spring application context.
In addition to that the scenario endpoint is automatically referenced when using the scenario designer instance. So in case you want to receive the scenario triggering message you do not need any endpoint reference.
When your scenario is not called this is because of some other issue in your setup. Maybe the incoming request does not map to your scenario definition.

WEB API 2 how to set Content-Type server side?

I have a customer that has a specific API design I am required to comply with. The logic I host with my WEB API code allows the customer to make simple changes to a resource that exists on my system (change, delete etc.).
The interface is very simple:
public IHttpActionResult Post(OpRequest opRequest)
public class OpRequest
{
public string op { get; set; }
public string data { get; set; }
}
Based on the value of "op", i parse "data" to complete the operation.
My question is related to the Content-type header in their request. They do not send a Content-Type header at all, but the actual data they POST is "application/x-www-form-urlencoded" for some requests and "application/json". for other requests. Works fine when they send urlencoded, but throws "415 unsupported media" error when they send JSON.
My thought is that I need to intercept their request, detect the content-type and set it before it reaches my logic, but I am not certain how to do that. I must use a single operation to accommodate all Content-types. Is this possible?

create a Jax-RS RESTful service that accepts both POST and GET?

I'm converting one of my existing service to become RESTful and I've got the basic things working with RestEasy. Some of my client apps should be able to execute both GET and POST requests to several services. I'm just seeking if there is any easy way around jax-rs to specify that API should accept both GETs and POSTs. Following you can find a test method, let me know if you see any way around without duplicating this in another class with #GET and #QueryParam.
#POST
#Path("/add")
public Response testREST(#FormParam("paraA") String paraA,
#FormParam("paraB") int paraB) {
return Response.status(200)
.entity("Test my input : " + paraA + ", age : " + paraB)
.build();
}
Just put your method body in another method and declare a public method for each HTTP verb:
#Controller
#Path("/foo-controller")
public class MyController {
#GET
#Path("/thing")
public Response getStuff() {
return doStuff();
}
#POST
#Path("/thing")
public Response postStuff() {
return doStuff();
}
private Response doStuff() {
// Do the stuff...
return Response.status(200)
.entity("Done")
.build();
}
}
As wikipedia says, an API is RESTful if it is a collection of resources with four defined aspects:
the base URI for the web service, such as http://example.com/resources/
the Internet media type of the data supported by the web service. This is often XML but can be any other valid Internet media type providing that it is a valid hypertext standard.
the set of operations supported by the web service using HTTP methods (e.g., GET, PUT, POST, or DELETE).
The API must be hypertext driven.
By diminishing the difference between GET and POST you're violating the third aspect.
If this scenario fits for all your resources you could create a ServletFilter which wraps the request and will return Get or Post everytime the method will be requested.

adding http headers in call to SoapHttpClient service

I have to consume a service provided by one of our partners. I was given little direction, but was told the security was to be PasswordDigest. I looked it up and immediatly saw lots of references to WSE, so off I went. It was very easy to implement and in no time I had a standard WSE user token using PasswordDigest sitting in the SOAP headers of my messages.
When we started testing today I was immediatly told (by the error message) that things weren't right. Turns out, out partner doesn't look in the SOAP header, but rather wants the security info in the http header.
I have seen lots of articles on how to add custom http headers to a proxy class, but my proxy inherits from SoapHttpClientProtocol which doesn't have a headers collection to add to. I was looking at making a raw httpWebRequest, but I have a specific method to access that has some complex parameters to deal with (and besides it feels like going backwords).
What is the best way to add custom http headers to a service proxy class that doesn't have a GetWebRequest method?
For reference:
Proxy class decleration:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "2.0.50727.3053")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name="MtomServiceSoap11", namespace="http://ws.xxxxxxx.com/")]
public partial class MtomServiceService : System.Web.Services.Protocols.SoapHttpClientProtocol {
Target method I need to call:
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Bare)]
[return: System.Xml.Serialization.XmlElementAttribute("uploadDocumentResponse", Namespace="http://ws.edsmtom.citizensfla.com/")]
public uploadDocumentResponse uploadDocument([System.Xml.Serialization.XmlElementAttribute(Namespace="http://ws.xxxxxxx.com/")] uploadDocumentRequest uploadDocumentRequest) {
object[] results = this.Invoke("uploadDocument", new object[] {
uploadDocumentRequest});
return ((uploadDocumentResponse)(results[0]));
}
}
The actual call to the Service is simple. The objects being pass in are not:
request.criteria = docCriteria;
request.document = document;
var result = service.uploadDocument(request);
Thanks.
It figures that 30 minutes after posting I would stumble across the answer. While the proxy class decelaration does not create a GetWebRequest method, its base class System.Web.Services.Protocols.SoapHttpClientProtocol has it and it can be overridden.
protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
var request = base.GetWebRequest(uri);
request.Headers.Add("blah", "blah"); // <----
return request;
}