Overriding GetWebRequest and accessing the Headers works just fine. Now I need to set an attribute. My goal looks like
<soapenv:Envelope xmlns:urn="urn:company:sap:ds:sales" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<messageId xmlns="http://www.company.com/foo/bar/soap/features/messageId/">urn:uuid:123c155c-3ab4-19ca-a045-02003b1bb7f5</messageId>
</soapenv:Header>
<soapenv:Body>
...
</soapenv:Body>
</soapenv:Envelope>
The problem is how to generate the xmlns="http://www.company.com/foo/bar/soap/features/messageId" attribute in the Header. I just spent a few hours reading documentation, and it seems there simply is no way to set an attribute in the header.
Could I simply quote the quotes and write
request.Headers["messageID xmlns=""http://www.company.com/foo/bar/soap/features/messageId"""] = "urn:uuid:123c155c-3ab4-19ca-a045-02003b1bb7f5";
But this feels somehow wrong to me..
please refer to the below code snippets, wish it is useful to you.
class Program
{
static void Main(string[] args)
{
ServiceReference1.ServiceClient client = new ServiceClient();
using (new OperationContextScope(client.InnerChannel))
{
UserInfo userInfo = new UserInfo();
userInfo.FirstName = "John";
userInfo.LastName = "Doe";
MessageHeader aMessageHeader = MessageHeader.CreateHeader("UserInfo", "http://tempuri.org", userInfo);
MessageHeader bheader = MessageHeader.CreateHeader("messageId", "http://www.company.com/foo/bar/soap/features/messageId/", "urn:uuid:123c155c-3ab4-19ca-a045-02003b1bb7f5");
OperationContext.Current.OutgoingMessageHeaders.Add(aMessageHeader);
OperationContext.Current.OutgoingMessageHeaders.Add(bheader);
var result = client.Test();
Console.WriteLine(result);
}
}
}
public class UserInfo
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Result.
This is an implementation on the client-side, you can also use IClientMessageInspector or IDispatchMessageInspector interface to add the custom header on the client-side and server-side.
Feel free to let me know if there is anything I can help with.
Related
I'm tasked with creating a web service that conforms to a particular wsdl and I haven't used SOAP or asmx before.
When I create a request in SoapUI I get the following structure, which is the same as the client will be using to send requests. (using placeholder names)
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:par="http://www.foo.com/schemas/method">
<soapenv:Header>
<par:SOAPHeaderRequest>
<par:ApplicationID>ID</par:ApplicationID>
</par:SOAPHeaderRequest>
</soapenv:Header>
<soapenv:Body>
<par:Body>
</par:Body>
</soapenv:Body>
</soapenv:Envelope>
However, when I'm trying to create the service I have this structure:
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Method xmlns="http://www.foo.com/schemas/method">
<request>
<SOAPHeaderRequest>
<ApplicationID>string</ApplicationID>
</SOAPHeaderRequest>
<body>
<Property>string</Property>
</body>
</request>
</Method>
</soap:Body>
</soap:Envelope>
I'd like to know how to remove the Method node wrapper, and how to move the SOAPHeaderREquest into a soap:Header.
Here's my sample code:
interface and objects
[ServiceContract(Namespace = "http://www.foo.com/schemas/method")]
public interface IServiceContract
{
[XmlSerializerFormat]
[OperationContract]
ResponseObject Method(RequestObject request);
}
[System.Serializable()]
[System.Xml.Serialization.XmlType(AnonymousType = true, Namespace = "http://www.foo.com/schemas/method")]
[MessageContract(IsWrapped = false)]
public class RequestObject
{
[System.ServiceModel.MessageHeader(Namespace = "http://www.foo.com/schemas/method")]
public SOAPHeaderRequest SOAPHeaderRequest;
[System.ServiceModel.MessageBodyMember(Namespace = "http://www.foo.com/schemas/method", Order = 0)]
public Body body;
public RequestObject()
{
}
public RequestObject(SOAPHeaderRequest SOAPHeaderRequest, Body body)
{
this.body = body;
}
}
[System.Serializable()]
[System.Xml.Serialization.XmlType(AnonymousType = true, Namespace = "http://www.foo.com/schemas/method")]
[MessageContract(IsWrapped = false)]
public class ResponseObject
{
[System.ServiceModel.MessageHeader(Namespace = "http://www.foo.com/schemas/method")]
public SOAPHeaderResponse SOAPHeaderResponse;
[System.ServiceModel.MessageBodyMember(Namespace = "http://www.foo.com/schemas/method", Order = 0)]
public Body body;
}
[System.Serializable()]
public class Body
{
public string Property { get; set; }
}
asmx
[WebService(Namespace = "http://www.foo.com/schemas/method")]
[WebServiceBinding(ConformsTo = WsiProfiles.None)]
public class M5NapaPartUpdateService : WebService, IServiceContract
{
[WebMethod]
[SoapMethod(SoapAction = "method")]
public ResponseObject Method(RequestObject request)
{
return new ResponseObject();
}
}
Let me know if there's anything else you'd need.
Thanks!
WSDL distinguishes between two message styles:
document and RPC.
The message style affects the contents of the SOAP Body:
Document style: The SOAP Body contains one or more child elements called parts. There are no SOAP formatting rules for what the body contains; it contains whatever the sender and the receiver agrees upon.
**RPC style:**RPC implies that SOAP body contains an element with the name of the method or operation being invoked. This element in turn contains an element for each parameter of that method/operation.
Your wsdl is written in Document Literal style.
If you are using service contract then I believe you are using WCF framework to write service code.
You can specify below parameters to make WSDL as you expect.
[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples"), XmlSerializerFormat(Style = OperationFormatStyle.Document,
Use = OperationFormatUse.Literal)]
Reference- https://www.ibm.com/support/knowledgecenter/en/SSB27H_6.2.0/fa2ws_ovw_soap_syntax_lit.html
Hope this helps.
I have a WebApi I built that sends emails. I also have a console application that runs everynight, generates a basic report, and emails it to me, through the API.
It was working perfect, until randomly one day I stopped getting the emails. (I say randomly, but I'm sure there was something that happened, - that's why I'm here.) If I send a short HtmlMessage, like <h1>Hi!</h1> it works, but the longer email it actually generates hits the server as null. I'm not sure if I made a change or something that broke this, but I definitely didn't change anything in the email's html.
I have a Mailer class:
public class Mailer
{
public string From { get; set; }
public string To { get; set; }
public string Subject { get; set; }
public string HtmlMessage { get; set; }
}
Here is my WebAPI:
[HttpPost]
[Route("api/sendmail")]
public void Sendmail(Mailer mailer) //public void Sendmail([FromBody] Mailer mailer) tried with and without [FromBody] and neither work
{
/* A bunch of code that doesn't matter */
}
And here is the code that calls the API:
static void Main() {
string message;
/* a bunch of stuff that generates the message */
SendEmail(message);
}
static void SendEmail(string message) {
var data = new Mailer { From = "foo#foo.com", To = "timothy#foo.com", Subject = "Daily Report", HtmlMessage = message };
var data2 = new Mailer { From = "foo#foo.com", To = "timothy#foo.com", Subject = "Daily Report", HtmlMessage = "<h1 style=\"color: red;\">HI</h1>" };
// I was using new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(data); but changed to JSON.net as an attempt to fix
var json = Newtonsoft.Json.JsonConvert.SerializeObject(data); // THIS DOES NOT WORK?! mailer in Sendmail is null.
//var json = Newtonsoft.Json.JsonConvert.SerializeObject(data2); // THIS WORKS?!
var url = "https://server.com/api/sendmail";
using (var client = new WebClient())
{
client.Headers.Add(_headers);
client.Headers.Add("Content-Type", "application/json");
client.UploadString(url, json);
}
}
Any help is appreciated! Thank you in advance!
Well, I feel dumb, but I was able to figure it out. I have a disclaimer at the bottom of the email, where I originally had (C) but replaced it with a copyright sign (©). That appears to have broken it. I replaced it with © and it works perfect now.
So, the issue was this character, that I assume WebAPI declined or was unable to deserialize into the Mailer class.
Anyways, it's fixed! Hopefully this helps someone else out down the road!
I am trying to call a rest api method from c#. Problem is for all content types it passes null to body parameter.I shared my code below.Apart from this code I have tried to write body parameter to request as stream.It didn't work either. I have also tried 'application/x-www-form-urlencoded' as content type.
Calling rest api method from c# sample:
string token = Server.UrlEncode("v0WE/49uN1/voNwVA1Mb0MiMrMHjFunE2KgH3keKlIqei3b77BzTmsk9OIREken1hO9guP3qd4ipCBQeBO4jiQ==");
string url = "http://localhost:2323/api/Applications/StartProcess?token=" + token;
string data = #"{""ProcessParameters"": [{ ""Name"":""flowStarter"",""Value"": ""Waffles"" }],
""Process"": ""RESTAPISUB""}";
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
client.BaseAddress = new System.Uri(url);
byte[] cred = UTF8Encoding.UTF8.GetBytes("username:password");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(cred));
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
System.Net.Http.HttpContent content = new StringContent(data, UTF8Encoding.UTF8, "application/json");
HttpResponseMessage messge = client.PostAsync(url, content).Result;
string description = string.Empty;
if (messge.IsSuccessStatusCode)
{
string result = messge.Content.ReadAsStringAsync().Result;
description = result;
}
Rest api Method:
[HttpPost]
[ActionName("StartProcess")]
public int StartProcess([FromUri]string token,[FromBody]WorkflowStartParameters parameters)
{
try
{
LoginInformation info = CasheProcesses.ReadCashe(token);
eBAWSAPI api = Service.GetWSService();
WorkflowProcess proc = api.StartProcess(info.Id, info.Password, info.ImpersonateUserId, info.Language, parameters);
return proc.ProcessId;
}
catch (Exception ex)
{
throw new Exception("An error occured when starting process,exception detail:" + ex);
}
}
WorkflowStartParameters class structure:
public class WorkflowStartParameters
{
public WorkflowParameter[] ProcessParameters;
public string Process { get; set; }
}
public class WorkflowParameter
{
public string Name { get; set; }
public string Value { get; set; }
}
I have searched this problem a lot. It seems as a very common problem. I just found this solution working properly, passing request parameter to rest api method and reading body parameter from there. But it is not a valid solution for me.
If you have any idea,feel free to share.
Thanks,
Zehra
I don´t know if it can solve your problem, but let me try.
I guess you don´t have to utilize Server.UrlEncode in your call, but:
Dim myUri As New Uri(Token)
And I guess you must not encode also your username and password - try pass them as string.
Your problem appear to be here:
public class WorkflowStartParameters
{
public WorkflowParameter[] ProcessParameters; <---- needs get/set
public string Process { get; set; }
}
This needs to be a public property to serialize properly. Currently you have it set up as a public field. Just add { get; set; } and give that a try. I would also look into serializing with Newtonsoft.Json to ensure your object is properly serialized. Trying to do it with escape strings will be messing the more data you are sending.
By the way there can be issues sometimes serializing arrays, I would change that to :
public List<WorkflowParameter> ProcessParameters{get;set;}
Finally I have achieved to send filled out data to server. It was about serialization problem. But it didn't work with json serialization before send data. I have added DataContract attribute to my class and it works properly.
Unfortunately still I couldn't figure out this when I make ajax calls from java script it works without DataContract attribute but if I call it in c# it needs DataContract attribute. If someone share the information about this I would appreciate!
I am sharing new class structure, everything else but this still same:
[Serializable]
[DataContract]
public class WorkflowParameter
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Value { get; set; }
}
[Serializable]
[DataContract]
public class WorkflowStartParameters
{
[DataMember]
public WorkflowParameter[] ProcessParameters { get; set; }
[DataMember]
public string Process { get; set; }
}
I am using the following to make a call to a WebAPI
using (HttpClient client = HttpClientFactory.Create(new AuthorisationHandler()))
{
client.BaseAddress = new Uri(BaseURI);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/xml"));
var httpResponseMessage = await client.PostAsXmlAsync<AvailRequest>("Avail/Search/", req);
httpResponseMessage.EnsureSuccessStatusCode();
var availResp = await httpResponseMessage.Content.ReadAsAsync<AvailResponse>();
return availResp;
}
the AvailResponse class looks something like this
[DataContract(Namespace = "")]
public class AvailResponse
{
[DataMember]
public ICollection<NotWorkingType> NotWorking { get; set; }
[DataMember]
public ICollection<WorkingType> Working { get; set; }
}
for some reason - clearly unknown to me - when the response comes in and is parsed into the AvailResponse object only the WorkingType is de-serialised and the other NotWorking one is not. I have used fiddler and can confirm that the response has both these in i.
I have tried using a XmlMediaTypeFormatter in place of the default and even setting the UseXmlSerialiser to true, but to no avail.
could someone shed some light on what is going on please
I would have thought that if it is not going to deserialise properly it would chuck and error rather than simply deserialising a part of the response.
any help as ever much appreciated
thanks
nat
I have a method GetColors which takes a GetColorIdsRQ as a parameter and returns a GetColorIdsRS. GetColorIdsRQ is the SOAP Request and GetColorIdsRS is the SOAP Resposne. Here are the implementation details of each one:
GetColorIdsRQ:
[DataContract]
public class GetColorIdsRQ
{
[DataMember(Name="UserCredentials",Order=0,IsRequired=true)]
public UserCredentials UserCredentials { get; set; }
}
UserCredentials:
[DataContract(Name="UserCredentials")]
public class UserCredentials
{
[DataMember(Name="Username",Order=0,IsRequired=true)]
public string UserName { get; set; }
[DataMember(Name="Password",Order=1,IsRequired=true)]
public string Password { get; set; }
}
GetColorIdsRS:
[DataContract]
public class GetColorIdsRS
{
[DataMember(Name = "ColorsIds", IsRequired = true, Order = 0)]
public List<Color> ColorIds { get; set; }
}
Color:
[DataContract(Name="Color")]
public class Color
{
[DataMember(Name="Code",IsRequired=true,Order=0)]
public string Code { get; set; }
[DataMember(Name="Name",IsRequired=true,Order=1)]
public string Name { get; set; }
}
This is how the method is declaration in the interface:
[OperationContract(Name = "GetColorIds")]
GetColorIdsRS GetColorsIds(GetColorIdsRQ req);
This is the implementation of GetColorIds:
public GetColorIdsRS GetColors(GetColorIdsRQ req)
{
GetColorIdsRS getColorIdsRS = new GetColorIdsRS();
List<Color> colorIds = new List<Color>();
req.UserCredentials.UserName = "test";
req.UserCredentials.Password = "test";
//Validate Username and Password
DataTable dtColorIds = Data.GetDataTable("GetColors");
foreach (DataRow item in dtColorIds.Rows)
{
colorIds.Add(new Color { Name = item["Name"].ToString(),
Code = item["ColorId"].ToString() });
}
getColorIdsRS.ColorIds = colorIds;
return getColorIdsRS;
}
When I invoke GetColors from the WCF Test client, the Request Body is:
<s:Body>
<GetColors xmlns="http://test.com/2011/05">
<req xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<UserCredentials>
<Username>test</Username>
<Password>test2</Password>
</UserCredentials>
</req>
</GetColors>
</s:Body>
The problem with the above is that I want to use the Username and Password nodes for the CustomUserNameValidator. I am not sure how to get the CustomUserNameValidator to recoginize GetColorIdsRQ Username and Password nodes so it can validate against that. If you notice above in the GetColors method, I am setting:
req.UserCredentials.UserName = "test";
req.UserCredentials.Password = "test";
but this doesn't get Validated obviously. Here is the implementation of my CustomUserNamePasswordValidator:
public class CustomUserNameValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
// check if the user is not test
if (userName != "test" || password != "test")
throw new FaultException("Username and Password Failed");
}
}
So essentially if I pass:
req.UserCredentials.UserName = "test";
req.UserCredentials.Password = "test2";
The CustomUserNameValidator should throw the FaultException.
Notice also in the GetColors method, I have a comment "//Validate Username and Password", I know I can do:
CustomUserNameValidator val = new CustomUserNameValidator();
val.Validate(req.UserCredentials.UserName,req.UserCredentials.Password)
and the above would call the Validate method, but I was made aware that it should be automatic and then I would have to do this in every single method.
Is it true that the only way for the CustomUserNameValidator to get called is to set the ClientCredentials in proxy code such as:
proxy.ClientCredentials.UserName.UserName = "test;
proxy.ClientCredentials.UserName.Password = "test;
The above would cause the Validate method to be called, but if I am unable to do the above and I set the Username and Password as properties of the Request object, then Validate won't be called, so my other option is to just have my own Validate method within each operation that requires it. An operation will not be called if the ClientCredentials fail, but in my case, I need an operation to call and then for it to return an SOAP Response with an error node with something like "Invalid Username and/or Password" instead of a throwing a FaultException.
Based on the above, is it best to avoid using the CustomUserNamePasswordValidator in my case?
In the event that the username and password can't be set through the proxy's client credentials, but is rather sent through the body of the soap request, then it appears that the way to handle this is to handle validation on an operational basis and does this affect the security settings (for example, is there a point to have a clientCredentialType="Username")
That is not possible unless you dive deeply into WCF security pipeline and implement custom security token (even then you can find that it is not possible) and all the stuff related to this implementation - it is a lot of work.
Why don't you use standard UserName authentication?
Edit:
If you want to pass credentials in custom element in message body (and you will not change security pipeline), the validation is up to you - it will not be routed to user name validator. You must do it either in operation or you can build custom message inspector (implement IMessageInspector) and wrap it with custom endpoint behavior (IEndpointBehavior) to have validation centralized.