How do I access SOAP headers in a Spyne srpc method? - soap

Forgive me if I am just being thick but the Spyne documentation on headers seems a bit thin and the link to the sample code is broken. I think I found the sample code here but it only seems to access the headers in a listener callback function. I need access to it in the srpc method if possible.
What I would like to do is something like this (this does not work):
class MyRequest(ComplexModel):
Messages = Array(Unicode)
class MyHeader(ComplexModel):
TrackingID = ByteArray
class MySoapService(ServiceBase):
#srpc(MyRequest, _in_header=MyHeader)
def PostMessages(req, hdr):
logging.info(u'RECEIVED: {0:s}'.format(hdr.TrackingID))
If it helps, I am trying to replace a service written in .NET that just defines the message headers in the message contract like this:
[MessageContract]
public class MyRequest
{
[MessageHeader]
public Guid TrackingID { get; set; }
[MessageBodyMember]
public String[] Messages { get; set; }
}

You can't read header data from within an srpc function. #srpc is for existing functions that you don't want to touch. You should always use #rpc unless you know you need #srpc
Decorate your function using #rpc. This passes the rpc context (conventionally named ctx) as first argument to your function. Then, ctx.in_header is where you'll find the header data. You can also set ctx.out_header to alter outgoing response headers.
Also, pull requests for docs are pure gold to me :)

Related

In Spring's rest controller (marked with #RestController), functions can return both model objects and a ResponseEntity object, which is better?

As a Spring Boot noob, I just know that in the controller class, its public functions can return both model objects and ResponseEntity object. Something like
public List<Book> getBooks() {}
or
public ResponseEntity<Book> getBooks() {}
But my question is which is better when there are multiple choices?
Basically, you have control over the HTTP response status if you use ResponseEntity, in addition to the content of the object itself.
public ResponseEntity<Object> getObject() {
return new ResponseEntity<Object>(object, Httpstatus.OK);
}
For example, if you need to validate some data from the request before executing any action and you want to let your client know what happened through the HTTP status code you can choose between different options.
HttpStatus.CONFLICT
HttpStatus.CREATED
Here you can take a look on the different status codes:
HTTP Status Codes
Just to shed more light on what #lbpeppers has mentioned. Using a ResponseEntity has many advantages.
1. The client need not look into the body of the message if the status code is something like 400 or 404, which is quite helpful
2. In some cases the client is not interested in the body. All it needs is a status of the operation
3. There are a lot of handy methods like is1xxInformational, is2xxSuccessful, is3xxRedirection, is4xxClientError, is5xxServerError in HttpStatus calss which can be used like
response.getStatusCode().is2xxSuccessful()

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?

WCF - Entity Framework - ERR_CONNECTION_RESET

I got a problem with my WCF service. Here is the
[OperationContract]
[WebGet(UriTemplate = "/needs", ResponseFormat = WebMessageFormat.Json)]
List<CustomerNeed> getAllCustomerNeeds();
When I go on the page which call this service, I got this error
GET http://localhost:666/rest/Service1.svc/needs net::ERR_CONNECTION_RESET
When I'm trying to return a string instead of a List, it works.
CustomerNeed is a class generate from my database via EntityFramework.
In my service, I'm only calling an other method which is in an other class;
public List<CustomerNeed> getAllCustomerNeeds()
{
var needs = from cn in db.CustomerNeeds
select cn;
List<CustomerNeed> list = new List<CustomerNeed>();
foreach (CustomerNeed cusN in needs)
{
list.Add(cusN);
}
return list;
}
Maybe is it because I have a foreign key in my table CustomerNeed ?
When I do "LINQ to entities" to import my database, do I have to import tables that were created because of many to many relation ?
I will recommend you to create a simple custom class which will represent your CustomerNeeds database entity, initiate this object on the server side and pass to the client application. It can help you to avoid this problem and also it is recommended way to transfer data accross the WCF services.
In this case you need to do the next steps:
1) Create a public class CustomerNeeds and mark it with the DataContract attribute. For example:
[DataContract]
public class CustomerNeeds
{
[DataMember]
public SomeDataType PropertyName {get; set;}
}
2) Initiate this object on the service, change return datatype in getAllCustomerNeeds() method from the entity class to the newly created class CustomerNeed and pass this data to the clien
And that`s all.
You haven't shown where/what db is, but I'm assuming if you're using entity framework as your tag implies it's a entities context. You might be having some issues with the context already being disposed or not newed up correctly (though I would have expected you to receive a slightly different error if that's the case.)
It looks like you're going through some unnecessary steps in your function, I would think something like this would work:
public List<CustomerNeed> getAllCustomerNeeds()
{
using (var db = new YourContext()) // plug in your context object
{
return db.CustomerNeeds.ToList();
}
}
Additionally when you say it "works as a string" are you returning something small like "hello world"? you might need to take a look at your WCF configuration to make sure it can handle the amount of data you're trying to pass back and forth.
Hope this helps!

Complex (non string) return type for Jersey REST method

I'm having trouble setting something up that I'm pretty sure /should/ be easy, so I thought I'd throw it to the crowd. I can't seem to find what I'm looking for elsewhere on the web or on SE.
I am simplifying my project of course, but basically I have a JAX-WS annontated Jersey resource class that looks something like this:
#Path("myresource")
public class MyResource {
#Autowired
MyComplexObjectDAO daoInstance;
#Path("findObject/{id}")
#GET
public MyComplexObject findObject( #PathParam(value="id") String id ) {
return daoInstance.findObject( id );
}
#Path("saveObject")
#PUT
public MyComplexObject saveObject( MyComplexObject objectToSave ) {
MyComplexObject savedObject = daoInstance.saveObject( objectToSave );
return savedObject;
}
}
So you can see I'm autowiring a DAO object using spring, and then I use the DAO methods in the REST handlers.
The 'findObject' call seems to work fine - so far it works exactly as I expect it to.
The 'saveObject' call is not working the way I want and that's what I need some advice on.
You can see that I'm trying to directly take an instance of my complex object as a parameter to the REST method. Additionally I would like to return an instance of the complex object after it's been saved.
I put together some 'client' code for testing this out.
#Test
public void saveTest() {
WebResource wsClient = createWebServiceClient();
MyComplexObject unsavedInstance = createMyComplexObject();
MyComplexObject savedInstance =
wsClient
.path("saveObject")
.accept(MediaType.APPLICATION_XML)
.put(MyComplexObject.class, unsavedInstance);
assertNotNull(savedIntent);
}
Which is returning the following error:
com.sun.jersey.api.client.UniformInterfaceException: PUT http://localhost:8081/rest/myresource/save returned a response status of 400 Bad Request
I don't see why this isn't working and I think I've tried just about everything I can think of. Any help or direction would be very much appreciated.
Thanks so much!
I see that you call the accept() method in your test client (which means that a "Accept:" header is added to the request, indicating the server what type of representation you would like). However, you don't call the type() method to add a "Content-type:" header and inform the server that you are sending XML data. See http://jersey.java.net/nonav/documentation/latest/client-api.html#d4e644 for examples.
Side remark: your URLs are not RESTful - you should avoid verbs in your path:
So, instead of:
/api/findObject/{id}
/api/saveObject
You should use:
/api/objects/{id}
/api/objects
Last note: to create an object on calling /api/objects, you should do a POST and not a PUT to adhere to REST best practices and widely adopted patterns.
switching to the 'concrete class' solution I alluded to in my earlier comment is what fixed things up for me.

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;
}