Help Please. I was trying to access WCF service exposed to a service bus relay endpoint using HttpWebRequest.
I successfully got a token from ACS via OAuth WRAP Protocol. Using that token as a authorization in a request header, I created a WebRequest to communicate with the WCF service with an endpoint configured as WebHttpRelayBinding and a WCF service method applied with OperationContractAttribute and WebGetAttribute.
When I run the client application I got following error:
The message with To
'https://namespace.servicebus.windows.net/Student/GetInfo/GetStudentInfo/1'
cannot be processed at the receiver, due to an AddressFilter mismatch
at the EndpointDispatcher. Check that the sender and receiver's
EndpointAddresses agree.
I googled and found a suggestion to apply following attribute to the service class:
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
Although this resolved the previous error, but now the client application is ending up with following error:
The message with Action 'GET' cannot be processed at the receiver, due
to a ContractFilter mismatch at the EndpointDispatcher. This may be
because of either a contract mismatch (mismatched Actions between
sender and receiver) or a binding/security mismatch between the sender
and the receiver. Check that sender and receiver have the same
contract and the same binding (including security requirements, e.g.
Message, Transport, None).
I think I am missing something on the WCF service side. Sharing both client and service code for your review.
WCF Service Code:
[ServiceContract]
interface IStudentInfo
{
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Xml, UriTemplate = "/GetStudentInfo/{studentId}")]
string GetStudentInfo(string studentId);
}
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
private class StudentInfo : IStudentInfo
{
string IStudentInfo.GetStudentInfo(string studentId)
{
string returnString = null;
// .....
return returnString;
}
}
public void Run()
{
Console.WriteLine("LISTENER");
Console.WriteLine("========");
string serviceNamespace = "namespace";
string issuerName = "owner";
string issuerKey = "key";
string servicePath = "Student/GetInfo";
ServiceHost sh = new ServiceHost(typeof(StudentInfo));
// Binding
WebHttpRelayBinding binding2 = new WebHttpRelayBinding();
Uri uri = ServiceBusEnvironment.CreateServiceUri(Uri.UriSchemeHttps, serviceNamespace, servicePath);
Console.WriteLine("Service Uri: " + uri);
Console.WriteLine();
sh.AddServiceEndpoint(typeof(IStudentInfo), binding2, uri);
// Create the ServiceRegistrySettings behavior for the endpoint.
var serviceRegistrySettings = new ServiceRegistrySettings(DiscoveryType.Public);
// Create the shared secret credentials object for the endpoint matching the
// Azure access control services issuer
var sharedSecretServiceBusCredential = new TransportClientEndpointBehavior()
{
TokenProvider = TokenProvider.CreateSharedSecretTokenProvider(issuerName, issuerKey)
};
// Add the service bus credentials to all endpoints specified in configuration.
foreach (var endpoint in sh.Description.Endpoints)
{
endpoint.Behaviors.Add(serviceRegistrySettings);
endpoint.Behaviors.Add(sharedSecretServiceBusCredential);
}
sh.Open();
Console.WriteLine("Press ENTER to close");
Console.ReadLine();
sh.Close();
}
Service Consuming Code:
static void Main(string[] args)
{
var studentId = "1";
string _token = GetToken();
Console.WriteLine(_token);
// Create and configure the Request
var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://namespace.servicebus.windows.net/Student/GetInfo/GetStudentInfo/" + studentId);
httpWebRequest.ContentType = "text/json";
httpWebRequest.Method = "GET";
httpWebRequest.Headers.Add(HttpRequestHeader.Authorization, string.Format("WRAP access_token=\"{0}\"", _token));
// Get the response using the Request
HttpWebResponse response = httpWebRequest.GetResponse() as HttpWebResponse;
// Read the stream from the response object
Stream stream = response.GetResponseStream();
StreamReader reader = new StreamReader(stream);
// Read the result from the stream reader
string result = reader.ReadToEnd();
Console.WriteLine("Result: " + result);
Console.ReadLine();
}
static string GetToken()
{
string base_address = string.Format("https://namespace-sb.accesscontrol.windows.net");
string wrap_name = "owner";
string wrap_password = "key";
string wrap_scope = "http://namespace.servicebus.windows.net/";
WebClient client = new WebClient();
client.BaseAddress = base_address;
NameValueCollection values = new NameValueCollection();
values.Add("wrap_name", wrap_name);
values.Add("wrap_password", wrap_password);
values.Add("wrap_scope", wrap_scope);
byte[] responseBytes = client.UploadValues("WRAPv0.9", "POST", values);
string response = Encoding.UTF8.GetString(responseBytes);
string token = response.Split('&')
.Single(value => value.StartsWith("wrap_access_token="))
.Split('=')[1];
string _token = HttpUtility.UrlDecode(token);
return _token;
}
Finally it's done!
I was missing a WebHttpBehavior to the endpoint that is necessary to expose WCF service as a REST endpoint.
endpoint.Behaviors.Add(new WebHttpBehavior());
Or alternatively I may host a service in WebServiceHost instead of a `ServiceHost' to enable REST binding to work.
WebServiceHost sh = new WebServiceHost(typeof(StudentInfo));
Related
I'm trying to retrieve and read emails from my Outlook mail. Unfortunately my mailbox uses Authenticity, which I need to deal with. I have tried a mailbox that does not use Authenticity and the code works. I followed the instructions here https://www.emailarchitect.net/eagetmail/ex/c/22.aspx
(I used the library to read a mailbox that does not use OAuth). So I registered my application on Microsoft Azure as instructed (except for authentication, which was the last step). Unfortunately I get this error System.ComponentModel.Win32Exception
HResult=0x80004005 Message=System cannot find the specified file.
Source=System.Diagnostics.Process
I also tried another library
https://afterlogic.com/mailbee-net/docs/OAuth2MicrosoftRegularAccountsInstalledApps.html
But with the same result
It is larger project, so I will post method where I am getting the error. I will paste more code, if you will need it.
Feel free to ask.
Thanks for any advice.(The documentation is great, so I didn't want to change it)
const string clientID = "Client ID";
const string clientSecret = "client Secret";
const string scope = "https://outlook.office.com/IMAP.AccessAsUser.All%20https://outlook.office.com/POP.AccessAsUser.All%20offline_access%20email%20openid";
const string authUri = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
const string tokenUri = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
static int GetRandomUnusedPort()
{
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
listener.Stop();
return port;
}
async void DoOauthAndRetrieveEmail()
{
// Creates a redirect URI using an available port on the loopback address.
string redirectUri = string.Format("http://127.0.0.1:{0}/", GetRandomUnusedPort());
Console.WriteLine("redirect URI: " + redirectUri);
// Creates an HttpListener to listen for requests on that redirect URI.
var http = new HttpListener();
http.Prefixes.Add(redirectUri);
Console.WriteLine("Listening ...");
http.Start();
// Creates the OAuth 2.0 authorization request.
string authorizationRequest = string.Format("{0}?response_type=code&scope={1}&redirect_uri={2}&client_id={3}&prompt=login",
authUri,
scope,
Uri.EscapeDataString(redirectUri),
clientID
);
// Opens request in the browser.
//There is issue
System.Diagnostics.Process.Start(authorizationRequest);
// Waits for the OAuth authorization response.
var context = await http.GetContextAsync();
// Brings the Console to Focus.
BringConsoleToFront();
// Sends an HTTP response to the browser.
var response = context.Response;
string responseString = string.Format("<html><head></head><body>Please return to the app and close current window.</body></html>");
var buffer = Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
var responseOutput = response.OutputStream;
Task responseTask = responseOutput.WriteAsync(buffer, 0, buffer.Length).ContinueWith((task) =>
{
responseOutput.Close();
http.Stop();
Console.WriteLine("HTTP server stopped.");
});
// Checks for errors.
if (context.Request.QueryString.Get("error") != null)
{
Console.WriteLine(string.Format("OAuth authorization error: {0}.", context.Request.QueryString.Get("error")));
return;
}
if (context.Request.QueryString.Get("code") == null)
{
Console.WriteLine("Malformed authorization response. " + context.Request.QueryString);
return;
}
// extracts the code
var code = context.Request.QueryString.Get("code");
Console.WriteLine("Authorization code: " + code);
string responseText = await RequestAccessToken(code, redirectUri);
Console.WriteLine(responseText);
OAuthResponseParser parser = new OAuthResponseParser();
parser.Load(responseText);
var user = parser.EmailInIdToken;
var accessToken = parser.AccessToken;
Console.WriteLine("User: {0}", user);
Console.WriteLine("AccessToken: {0}", accessToken);
RetrieveMailWithXOAUTH2(user, accessToken);
}
I have an API which i want to get access from one of my proxy server only. As that server have given all access to access that particular API. So I have single endpoint.
I have proxy server URL and Port and i want to add proxy settings in my app settings file and implement it so when call is given to API, that particular call pass through the Proxy server.
Please assist me how can I achieve this?
Current Call to API as below.
PushMessageAndroidRequest req = new PushMessageAndroidRequest();
req.registration_ids = list.Select(x => x.Token).ToList();
req.data = new AndroidData() { Payload = CommonLib.ConvertObjectToJson(payload) };
response = await RequestHandler.PostDataAsync<PushMessageResponse>(_appConfig.pushMessageConfigs.Url, req, new List<KeyValue>() { new KeyValue("Authorization", "key=" + _appConfig.pushMessageConfigs.Key) });
Sample code have written
public static async Task<ResJsonOutput> ProxyDataAsync(string ApiPath,string obj, List<KeyValue> Headers = null)
{
ResJsonOutput result = new ResJsonOutput();
HttpResponseMessage response = new HttpResponseMessage();
var requestUri = string.Format(ApiPath);
var request = (HttpWebRequest)WebRequest.Create(requestUri);
WebProxy myproxy = new WebProxy(Settings.ProxyAddress, Settings.ProxyPort);
myproxy.BypassProxyOnLocal = false;
request.Proxy = myproxy;
using (WebResponse response = request.GetResponse())
{
using (StreamReader stream = new StreamReader(response.GetResponseStream()))
{
//JObject jResponse = JObject.Parse(stream.ReadToEnd());
//var isSuccess = jResponse.Value<bool>("success");
//result = (isSuccess) ? true : false;
}
}
return result;
}
The remote server returned an error: (415) Cannot process the message because the content type 'application / xml' was not the expected type 'application / soap + xml; charset = utf-8 '
I just try to launch my self host. I have 2 endpoints with Basic Authentication. So I had to use wsHttpBinding for that. CreateUser endpoint should use XML format and RemoveUser endpoint - json format.
I attached my selfhost app.config, client main function and contract.
server app.config
<services>
<service name="Web.Service.Core.Services.UserContract"
behaviorConfiguration="AuthBehavior" >
<endpoint address="CreateUser"
binding="wsHttpBinding"
bindingNamespace="http://localhost/Auth/"
contract="Web.Service.Library.Contracts.IUserContract" />
<endpoint address="RemoveUser"
binding="wsHttpBinding"
contract="Web.Service.Library.Contracts.IUserContract" />
IUserContract.cs
[ServiceContract(Namespace = "http://localhost/Auth/", ProtectionLevel = ProtectionLevel.None)]
[XmlSerializerFormat]
public interface IUserContract
{
[OperationContract]
[WebInvoke(Method = "POST",
UriTemplate = "Auth/CreateUser",
RequestFormat = WebMessageFormat.Xml,
ResponseFormat = WebMessageFormat.Xml,
BodyStyle = WebMessageBodyStyle.Wrapped)]
Response CreateUser(Stream xml);
[OperationContract]
[WebInvoke(Method = "POST",
UriTemplate = "Auth/RemoveUser",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped)]
Response RemoveUser(Stream stream);
client main()
var webRequest = (HttpWebRequest) WebRequest.Create(CreateUserUrl);
webRequest.Method = "POST";
webRequest.ContentType = "application/xml";
webRequest.ContentLength = data.Length;
var rqStream = webRequest.GetRequestStream();
rqStream.Write(data, 0, data.Length);
rqStream.Close();
var webResponse = webRequest.GetResponse();
var rsStream = webResponse.GetResponseStream();
var responseXml = new StreamReader(rsStream);
var s = responseXml.ReadToEnd();
When we use wshttpbinding to create WCF service, the service based on the web service speficification, and use Simple Object Access Protocol to communicate. We could check the communication details by using fiddler.
The content-type is Application/soap+xml instead of the Application/xml, and the request body format based on SOAP envelop.
https://en.wikipedia.org/wiki/SOAP
This kind of web service is called SOAP web service. Ordinarily, we call the service by using the client proxy class.
ServiceReference1.ServiceClient client = new ServiceClient();
try
{
var result = client.GetData();
Console.WriteLine(result);
}
catch (Exception)
{
throw;
}
https://learn.microsoft.com/en-us/dotnet/framework/wcf/accessing-services-using-a-wcf-client
The way you call a service usually applies to Restful-style service.
https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design
Instantiate the HttpClient class and custom the request body. Under this circumstance, we should use WebHttpBinding in WCF to create the service. Please refer to my reply.
How to fix "ERR_ABORTED 400 (Bad Request)" error with Jquery call to C# WCF service?
Feel free to let me know if there is anything I can help with.
Updated.
class Program
{
/// <summary>
/// https webhttpbinding.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
Uri uri = new Uri("https://localhost:4386");
WebHttpBinding binding = new WebHttpBinding();
binding.Security.Mode = WebHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
using (WebServiceHost sh = new WebServiceHost(typeof(TestService), uri))
{
sh.AddServiceEndpoint(typeof(ITestService), binding, "");
ServiceMetadataBehavior smb;
smb = sh.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (smb == null)
{
smb = new ServiceMetadataBehavior()
{
//HttpsGetEnabled = true
};
sh.Description.Behaviors.Add(smb);
}
Binding mexbinding = MetadataExchangeBindings.CreateMexHttpsBinding();
sh.AddServiceEndpoint(typeof(IMetadataExchange), mexbinding, "mex");
sh.Opened += delegate
{
Console.WriteLine("service is ready");
};
sh.Closed += delegate
{
Console.WriteLine("service is closed");
};
sh.Open();
Console.ReadLine();
sh.Close();
}
}
}
[ServiceContract]
public interface ITestService
{
[OperationContract]
[WebGet(ResponseFormat =WebMessageFormat.Json)]
string GetResult();
}
[ServiceBehavior]
public class TestService : ITestService
{
public string GetResult()
{
return $"Hello, busy World. {DateTime.Now.ToShortTimeString()}";
}
}
Bind a certificate to the port(Powershell command).
netsh http add sslcert ipport=0.0.0.0:4386 certhash=cbc81f77ed01a9784a12483030ccd497f01be71c App
id='{61466809-CD17-4E31-B87B-E89B003FABFA}'
Result.
I am calling a Rest API using a basic http authentication
public string Get(string LabName)
{
string userName = ConfigurationManager.AppSettings["username"];
string password = ConfigurationManager.AppSettings["password"];
string BaseURL = ConfigurationManager.AppSettings["BaseURL"];
using (var client = new HttpClient())
{
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback( delegate { return true; });
Uri uri = new Uri(BaseURL);
client.BaseAddress = uri;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
var byteArray = Encoding.ASCII.GetBytes(userName+":"+password);
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
string clarity_URL = BaseURL + "api/v2/labs?name=" + LabName;
var response = client.GetAsync(clarity_URL).Result;
string responseString = response.Content.ReadAsStringAsync().Result;
return responseString;
}
When I debug the code throws error on the line response like
Can anyone please suggest me what could be the issue.
A 500 Error usually means there is a problem with the API Server.
It would be a good idea to check the specific endpoint for any errors then check again with this code.
If you are checking against a web call that is working correctly, please ensure that the request method (GET / POST / PUT) is correctly aligned and the parameters match.
I am working on a Android/C# project. What I need to be able to do is have a WCF soap service which can either run on Windows or Linux (Mono).
It's working fine on Windows and I can access the WCF Service on Mono from the WCF Test Client provided in Visual Studio and it works fine but when accessing android using KSOAP2 I get the error HTTP Request Failed, HTTP status: 415
Below is how the soap service is started
string methodInfo = classDetails + MethodInfo.GetCurrentMethod().Name;
try
{
if (Environment.GetEnvironmentVariable("MONO_STRICT_MS_COMPLIANT") != "yes")
{
Environment.SetEnvironmentVariable("MONO_STRICT_MS_COMPLIANT", "yes");
}
if (String.IsNullOrEmpty(soapServerUrl))
{
string message = "Not starting Soap Server: URL or Port number is not set in config file";
library.logging(methodInfo, message);
library.setAlarm(message, CommonTasks.AlarmStatus.Medium, methodInfo);
return;
}
Console.WriteLine("Soap Server URL: {0}", soapServerUrl);
baseAddress = new Uri(soapServerUrl);
host = new ServiceHost(soapHandlerType, baseAddress);
BasicHttpBinding basicHttpBinding = new BasicHttpBinding();
//basicHttpBinding.Namespace = "http://tempuri.org/";
var meta = new ServiceMetadataBehavior()
{
HttpGetEnabled = true,
HttpGetUrl = new Uri("", UriKind.Relative),
//HttpGetBinding = basicHttpBinding,
};
//meta.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(meta);
host.AddServiceEndpoint(soapManagerInterface, basicHttpBinding, soapServerUrl);
host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
var debugBehaviour = new ServiceDebugBehavior()
{
HttpHelpPageEnabled = true,
HttpHelpPageUrl = new Uri("", UriKind.Relative),
IncludeExceptionDetailInFaults = true,
//HttpHelpPageBinding = basicHttpBinding,
};
host.Description.Behaviors.Remove(typeof(ServiceDebugBehavior));
host.Description.Behaviors.Add(debugBehaviour);
host.Opened += new EventHandler(host_Opened);
host.Faulted += new EventHandler(host_Faulted);
host.Closed += new EventHandler(host_Closed);
host.UnknownMessageReceived += new EventHandler<UnknownMessageReceivedEventArgs>(host_UnknownMessageReceived);
host.Open();
}
The soapServerURL is http://192.168.1.74:8000/CritiMon.
Below is how I am trying to call the soap service from android using KSOAP2.
final String soapAction = "http://tempuri.org/ISoapInterface/testSoapFunction";
final String namespace = "http://tempuri.org/";
final String methodName = "testSoapFunction";
final String url = "http://192.168.1.74:8000/CritiMon?wsdl";
String resultData = "";
new Thread(new Runnable() {
#Override
public void run() {
SoapSerializationEnvelope envelope = null;
try
{
//String resultData = "";
SoapObject request = new SoapObject(namespace, methodName);
//request.addProperty("firstName", "Chris");
//request.addProperty("lastName", "Board");
envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
HttpTransportSE http = new HttpTransportSE(url);
http.call(soapAction, envelope);
SoapPrimitive result = (SoapPrimitive)envelope.getResponse();
String resultData = result.toString();
Log.d("Soap Result", resultData);
}
catch (Exception ex)
{
Log.e("Soap Error 2", ex.getMessage());
}
I have no idea what I can do to make this work on Mono with Android.
Firstly, you want to catch the actual SOAP request on the wire. You can do this using Fiddler or SoapUI - they both act as proxies through which local requests are passed, allowing you to inspect the actual XML request for anomalies. You might discover something obvious by doing this, or you can at least update your question with more information.
Following that, and without any further information, I can only offer a memorable experience with talking to WCF services from non-.NET applications:
WCF specifies the XML request that it expects, but it actually requires object properties to be in a specific order. This can be a declared order in the datacontract, or it can be an implicit alphabetical order. Either way, if you don't provide object properties in the specified order you will be stung and things won't work.