Here is my wcf service method:
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "/CheckID/{id}")]
public string CheckID(string id)
{
/*Check reuqest where it comes from */
}
I want my method send response OK if it comes/is invoked from http://particularIP.com, unless response Bad request.
How can i do that?
You can use IP Filter in web.config file, like :-
<serviceBehaviors>
<behavior name="ServiceBehaviour">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
<behavior name="RestrictedServiceBehaviour">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<IPFilter filter="172.*.*.* 127.0.0.1" />
</behavior>
</serviceBehaviors>
Edited
Or use can ServiceAuthorizationManager.CheckAccessCore in which you get client IP from OperationContext.
https://msdn.microsoft.com/en-us/library/system.servicemodel.serviceauthorizationmanager.checkaccesscore.aspx
Edit 2
using System.ServiceModel;
using System.ServiceModel.Channels;
OperationContext context = OperationContext.Current;
MessageProperties prop = context.IncomingMessageProperties;
RemoteEndpointMessageProperty endpoint =
prop[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
string ip = endpoint.Address;
Related
I'm new to WCF and I wrote a sample WCF service which uses the InstanceContextMode. When I'm using the PerSession, my counter value doesn't gets incremented. Why doesn't it uses the same instance for every service call.
Below is my code
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class Service1 : IService1
{
int count;
public int Add(int a, int b)
{
count++;
return a + b;
}
public int GetCount()
{
return count;
}
}
WebConfig
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
Client.cs
DemoWCFService.Service1Client client = new DemoWCFService.Service1Client();
Console.WriteLine(""+ client.Add(10,20));
Console.WriteLine("" + client.Add(10, 20));
Console.WriteLine("" + client.Add(10, 20));
Console.WriteLine("" + client.GetCount());
Console.ReadKey();
Please help me with this.
BasicHttpBinding doesn't support PerSession.
You had better use binding that supports session such as WSHttpBinding, WS2007HttpBinding.
To ensure you are using binding that supports session, you could use a service behavior
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
If your binding doesn't support session, it will cause error.
I have a WCF Service with a SOAP endpoint. I added a REST endpoint and the Get methods are working just fine. I am having trouble with a POST method which takes in an object and returns a different object. When I pass in my object, I get this error back:
"Message":"Object reference not set to an instance of an object."
Here's the code to call the service:
string URL = "http://qa.acct.webservice";
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(URL);
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP POST
var request = new RequestGetInventory
{
BrandIDs = new string[] { "4", "42" },
AccountID = "9900003"
};
var resp = client.PostAsJsonAsync("/AxaptaService.svc/rest/GetInventory", request);
response = resp.Result;
if (response.IsSuccessStatusCode)
{
var temp = response.Content.ReadAsStringAsync().Result;
MessageBox.Show(temp); //error message received here.
}
The RequestGetInventory object is defined as follows:
[DataContract]
public class RequestGetInventory
{
[DataMember]
public string[] BrandIDs { get; set; }
[DataMember]
public string AccountID { get; set; }
}
The contract for the webservice is defined as follows:
[OperationContract]
[WebInvoke(Method = "POST",
RequestFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.WrappedRequest,
ResponseFormat = WebMessageFormat.Json)]
ResponseGetInventory GetInventory(RequestGetInventory Request);
I tried playing around with the WebInvoke parameters, but received the same error message for all viable attempts.
And this is how my web.config is set up:
<system.serviceModel>
<services>
<service behaviorConfiguration="" name="Proj.AxaptaUS.WebService.AxaptaService">
<endpoint address="rest" behaviorConfiguration="webBehavior" binding="webHttpBinding" contract="Proj.Interfaces.Axapta3.IAxaptaService"></endpoint>
<endpoint address="" binding="basicHttpBinding" contract="Proj.Interfaces.Axapta3.IAxaptaService"></endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="webBehavior">
<webHttp helpEnabled="true" />
<enableWebScript/>
</behavior>
</endpointBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
I am not entirely sure what I'm doing wrong because I can access this using SOAP just fine. It seems like it is not getting any values for the object which I passed in, thus causing the object reference error.
Any help would be greatly appreciated! Thanks!
#jstreet posted a comment which ended up working.
I changed BodyStyle = WebMessageBodyStyle.WrappedRequest to BodyStyle = WebMessageBodyStyle.Bare and removed <enableWebScript/> from config file.
After doing those things, it started to work correctly! thanks #jstreet!
I have this WCF service and I'm trying to apply authentication and authorization mechanism in it.
It's my first time to do this, what I have is this web.config serviceModel tag for the service:
<system.serviceModel>
<services>
<service name="RoleBasedServices.SecureServiceExternal" behaviorConfiguration="externalServiceBehavior">
<endpoint contract="AuthService.IService1" binding="wsHttpBinding" bindingConfiguration="wsHttpUsername" />
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="wsHttpUsername">
<security mode="Message">
<message clientCredentialType="UserName" negotiateServiceCredential="false" establishSecurityContext="false" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<!--To avoid disclosing metadata information, set the values below to false before deployment-->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!--To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information-->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
<behavior name="externalServiceBehavior">
<serviceAuthorization principalPermissionMode="UseAspNetRoles" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="MembershipProvider" />
<serviceCertificate findValue="RPKey" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
What I want to do is very simple, I don't know if I need all this tags I'm just trying.
What I want to do is from the client side to add reference for the service and first call the MyLogin:
AuthService.Service1Client s = new AuthService.Service1Client();
s.Login();
Then call the other restricted method and let it be GetData:
s.GetData()
At service side in Login method, and only for test purposes, I'm doing this:
public void Login()
{
Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity("Bob"), new[] { "Admin" });
FormsAuthentication.SetAuthCookie("BobUserName", false);
}
An the restricted method will be:
[PrincipalPermission(SecurityAction.Demand, Role = "Admin")]
public void GetData()
{
return "Hello";
}
That all I have in service and client, what I'm missing?
Every time ,in debug, I check Thread.CurrentPrincipal in Login method I found Thread.CurrentPrincipal.Identity.IsAuthenticated equals true but even though when the client calls the GetData() method it's Access Denied.
PS: I'm using console application to do my tests does it make any difference ?
Thanks
Here is a very nice article that could possibly lead to a solution.
The general idea is that you have 2 object for the Principal.
HttpContext.Current.User and Thread.CurrentPrincipal. You are setting the Thread.CurrentPrincipal at the time HttpContext.Current.User is already instantiated and the role of it is left to default. You may want to try something like:
HttpContext.Current.User = new GenericPrincipal(new GenericIdentity("Bob"), new[] { "Admin" });
The reason calls to GetData() are denied is because WCF doesn't know anything about the Forms Authentication cookie that was set during Login().
It doesn't make a difference that you're a using console app. You could try the following approach.
Set the cookie in Login():
var cookie = FormsAuthentication.GetAuthCookie(username, true);
var ticket = FormsAuthentication.Decrypt(cookie.Value);
HttpContext.Current.User = new GenericPrincipal(new FormsIdentity(ticket), null);
FormsAuthentication.SetAuthCookie(HttpContext.Current.User.Identity.Name, true);
Then in your console app:
public static void TestLoginAndGetData()
{
var sharedCookie = string.Empty;
using (var client = new YourClient())
using (new OperationContextScope(client.InnerChannel))
{
client.Login("username", "password");
// get the cookie from the response
HttpResponseMessageProperty response = (HttpResponseMessageProperty)
OperationContext.Current.IncomingMessageProperties[
HttpResponseMessageProperty.Name];
sharedCookie = response.Headers["Set-Cookie"];
// add it to the request
HttpRequestMessageProperty request = new HttpRequestMessageProperty();
request.Headers["Cookie"] = sharedCookie;
OperationContext.Current.OutgoingMessageProperties[
HttpRequestMessageProperty.Name] = request;
var result = client.GetData();
Console.WriteLine(result);
}
}
You might also consider changing the return type of GetData() to string.
I am trying to create a JSON WCF web service.
I'm totally unclear on the whole process really! I'm connecting to MySQL DB on my Server.
So I have the following code:
My Interface -
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped, UriTemplate = "/GetAllResources")]
List<Resources> GetAllResources();
[OperationContract]
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped, UriTemplate = "/AddRoom")]
void AddRoom(string location, string name);
...
My Service -
[ScriptService]
public class Service1 : IService1
{
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public void AddRoom(string location, string name)
{
String conString = System.Configuration.ConfigurationManager.ConnectionStrings["MyDatabaseConnectionString"].ConnectionString;
using (MySqlConnection cnn = new MySqlConnection(conString))
{
cnn.Open();
String sql = String.Format("INSERT INTO rooms(roomLocation, roomName) VALUES ({0}, {1});", location, name);
MySqlCommand cmd = new MySqlCommand(sql, cnn);
//doesn't return any rows
cmd.ExecuteNonQuery();
}
}
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public List<Resources> GetAllResources()
{
String conString = System.Configuration.ConfigurationManager.ConnectionStrings["MyDatabaseConnectionString"].ConnectionString;
List<Resources> al = new List<Resources>();
using (MySqlConnection cnn = new MySqlConnection(conString))
{
cnn.Open();
String sql = String.Format("select * from resources");
MySqlCommand cmd = new MySqlCommand(sql, cnn);
MySqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
al.Add((Resources)reader[0]);
}
return al;
}
}
...
Web Config -
...
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="5000"/>
</webServices>
</scripting>
</system.web.extensions>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5"/>
</system.web>
<system.serviceModel>
<services>
<service name="RoomBookingService.Service1" behaviorConfiguration="RoomBookingServiceBehavior">
<endpoint address="../Service1.svc"
binding="webHttpBinding"
contract="RoomBookingService.IService1"
behaviorConfiguration="webBehaviour" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="RoomBookingServiceBehavior">
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="webBehaviour">
<webHttp automaticFormatSelectionEnabled="true"/>
</behavior>
</endpointBehaviors>
</behaviors>
...
Is this correct?? What tools can I use to test the service? I have dropped it onto the server and tried downloading a few testing tools but they don't give me any errors just that it's not returning JSON?!
I will be creating an Android app to talk to the Service, but as this will also be a learning curve I want to know that my service works correctly before adding in another layer of complexity.
Any help or comments on my code or my issue would be greatly appreciated.
Thank you for your time
I managed to get it working:
Here is my code:
Contract:
namespace RoomBookingService
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped, UriTemplate = "GetAllResources")]
String GetAllResources();
Service
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public String GetAllResources()
{
String conString = System.Configuration.ConfigurationManager.ConnectionStrings["MyDatabaseConnectionString"].ConnectionString;
List<Dictionary<string, object>> tableRows = new List<Dictionary<string, object>>();
Dictionary<string, object> row= new Dictionary<string,object>();
DataTable dt = new DataTable();
System.Web.Script.Serialization.JavaScriptSerializer serializer =
new System.Web.Script.Serialization.JavaScriptSerializer();
try
{
using (MySqlConnection cnn = new MySqlConnection(conString))
{
cnn.Open();
String sql = String.Format("select resourceID, resourceName, resourceDesc, roomID from resources");
MySqlCommand cmd = new MySqlCommand(sql, cnn);
MySqlDataReader reader = cmd.ExecuteReader();
dt.Load(reader);
foreach (DataRow dr in dt.Rows)
{
row = new Dictionary<String, Object>();
foreach (DataColumn col in dt.Columns)
{
row.Add(col.ColumnName, dr[col]);
}
tableRows.Add(row);
}
return serializer.Serialize(tableRows);
}
}
catch (Exception ex)
{
return ex.ToString();
}
}
WebConfig
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="5000"/>
</webServices>
</scripting>
</system.web.extensions>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5"/>
</system.web>
<system.serviceModel>
<services>
<service name="RoomBookingService.Service1" behaviorConfiguration="RoomBookingServiceBehavior">
<endpoint address=""
binding="webHttpBinding"
contract="RoomBookingService.IService1"
behaviorConfiguration="webBehaviour" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="RoomBookingServiceBehavior">
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="webBehaviour">
<webHttp automaticFormatSelectionEnabled="true"/>
</behavior>
</endpointBehaviors>
Still not absultely clear on everything but it's working!! So I'll go with that :-)
Thanks!
I have the WCF service contract:
[ServiceContract]
public interface IVLSContentService
{
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Xml, UriTemplate = "GetCategoriesGET/{userIdArg}", BodyStyle = WebMessageBodyStyle.Bare)]
List<Video> GetVideosGET(string userIdArg);
[WebInvoke(Method = "POST",BodyStyle=WebMessageBodyStyle.Wrapped, UriTemplate = "submit")]
[OperationContract]
void SubmitVideoPOST(Video videoArg, string userId);
}
And I have the service that implements the contract:
public class VLSContentService : IVLSContentService
{
List<Video> catsForUser1 = new List<Video>();
List<Video> catsForUser2 = new List<Video>();
public List<Video> GetVideosGET(string userIdArg)
{
List<Video> catsToReturn = new List<Video>();
if (Int32.Parse(userIdArg) == 1)
{
catsToReturn = catsForUser1;
}
else if (Int32.Parse(userIdArg) == 2)
{
catsToReturn = catsForUser2;
}
return catsToReturn;
}
public void SubmitVideoPOST(Video videoArg, string userId)
{
int i = 0;
}
}
And I have the configuration:
<system.serviceModel>
<services>
<service behaviorConfiguration="VLSContentServiceBehaviour" name="VLSContentService">
<endpoint address="" behaviorConfiguration="VLSContentServiceEndpointBehaviour" binding="webHttpBinding" contract="IVLSContentService"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="VLSContentServiceBehaviour">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="VLSContentServiceEndpointBehaviour">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
And I am trying to call the POST WCF operation with the following client code:
static void Main(string[] args)
{
WebChannelFactory<IVLSContentService> cs = new WebChannelFactory<IVLSContentService>(new Uri("http://localhost:52587/Api/Content/VLSContentService.svc/SubmitVideoPOST"));
IVLSContentService client = cs.CreateChannel();
Video videoToAdd = new Video("My First Video");
client.SubmitVideoPOST(videoToAdd,"1");
}
But im getting this error and I cant work out why:
There was no endpoint listening at
http://localhost:52587/Api/Content/VLSContentService.svc/SubmitVideoPOST/submit
that could accept the message. This is
often caused by an incorrect address
or SOAP action. See InnerException, if
present, for more details.
I know when I browse to the GET method in a URL and I pass the correct parameters I am getting xml back but my POST method just doesnt work. Ive copied the example from pluralsight the only difference is um trying to host the service in .svc file instead of service host application...
Can anybody point me in the right direction?
Looks like you have the address of the service wrong
You should be posting to http://localhost:52587/Api/Content/VLSContentService.svc/submit
The UriTemplate is relative to the address of the endpoint which is
http://localhost:52587/Api/Content/VLSContentService.svc
Change this line of code to
WebChannelFactory cs = new WebChannelFactory(new Uri("http://localhost:52587/Api/Content/VLSContentService.svc/"));
You appear to be posting to the wrong URL. The error shows you posting to the relative address "/SubmitVideoPOST/submit", but your UriTemplate for that method is just "/submit".
You do not need to include the .NET method name in the URL for REST based requests. Only the UriTemplate matters. Mapping to the correct runtime method is done for you by the WCF REST UriTemplate processing engine.