I have a Wcf (ajax enabled) service, that accepts a object for the method call. My Wcf method looks like this;
[OperationContract]
[XmlSerializerFormat]
[WebInvoke(Method = "POST", UriTemplate = "/XML/GetTypes", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Xml)]
XElement XMLGetTypes(TypeRequest TypeRequest)
{
return Utilities.Convert.ObjectToXElement(TypeRequest.Execute(TypeRequest));
}
The 'TypeRequest' object is as follows;
public class
{
public int Id {get;set;}
public string Name {get; set;}
}
The problem I'm having is that if I call the method with invalid data like in the request object like;
<TypeRequest>
<Id>abc</Id>
<Name>Joe King</Name>
</TypeRequest>
As you can see the Id should be a integer, however it is being passed as a string. This causes a Bad Request 400 response from the Wcf service.
Ideally I'd like to handle the error, and return a suitable response. For example I'd like to return either a JSON or XML response containing the error information.
Is this possible ?
IIS allows you to create error handlers overall in the pipeline. How you handle the code and what you return is up to you, though I would be careful, as this handles all the errors for an IIS application.
public class ErrorHttpHandler : IHttpHandler
{
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
if (context.Request.QueryString.Count == 0)
return;
string strStatusCode = context.Request.QueryString[0].Split(';').FirstOrDefault() ?? "500";
int statusCode = 500;
int.TryParse(strStatusCode, out statusCode);
string message = "Unhandled server error.";
switch (statusCode)
{
case 400:
message = "Bad request.";
break;
case 404:
message = "Item not found.";
break;
}
context.Response.StatusCode = statusCode;
context.Response.Write(string.Format("<Error><Message>{0}</Message></Error>", message));
}
}
and in your web.config, add this code for the handler to be called:
<system.webServer>
<httpErrors errorMode="Custom">
<clear/>
<error statusCode="404" path="/application/ErrorHandler" responseMode="ExecuteURL"/>
<error statusCode="400" path="/application/ErrorHandler" responseMode="ExecuteURL"/>
</httpErrors>
<handlers>
<add name="ErrorHandler" path="ErrorHandler" verb="*" type="Your Application.ErrorHttpHandler, FrameworkAssembly"/>
</handlers>
</system.webServer>
All of this was culled from this site.
You can check your parameter using parameter inspector, which will allow you to throw fault exception with the message you need. Also you can provide your client (if it's .net client) with you Parameter inspector attribute. As a result the message will not be send till it pass validation, which can save you traffic. Here is a link:
WCF Parameter Validation with Interceptor
And in case your client send you a wrong message. You have to use message inspector:
MSDN, Logging all WCF messages
Here is even better example:
http://msdn.microsoft.com/en-us/library/ff647820.aspx
You have to put more attention for methods AfterReceiveRequest. Please notice that it's required to create a buffered copy of the message, then work with one copy of a message, and return another one.
it is possible to do this but you may need to do it a little differently, the easiest way may be to alter your incoming Type request as follows
public IncomingClass
{
public string Id {get;set;}
public string Name {get; set;}
}
this will mean that the incoming data is valid, and you won't get the error 400,
you can then validate this using
int myId = incomingClass.Id as int;
if (myId == null)
{
//put suitable error handling here
}
or perhaps better still stick the whole function in a try catch loop, and handle the error that way. I think you will find the error is happening before you are entering your function. your WCF Service is expecting xml that can be translated to the type of Object "TypeRequest" but your xml is invalid.
Hope that helps
I think you would need some method interceptors and write the expected logic in there.
Here's a good resource on that http://msdn.microsoft.com/en-us/magazine/cc163302.aspx
Related
I am developing an API using C# and .net 4.5.2; The API methods can return a handled BadRequest error or OK with an object response as per the below example:
[Authorize]
[RoutePrefix("api/Test")]
public class TestController : ApiController
{
[HttpGet]
[Route("TestMethod")]
public IHttpActionResult TestMethod()
{
MyProvider op = new MyProvider();
var lstResults = new List<Result>();
try
{
lstResults = op.TestMethod();
}
catch (Exception ex)
{
return BadRequest(ParseErrorMessage(ex.Message.ToString()));
}
return Ok(lstResults);
}
}
All errors are returned in a message object as below JSON:
{
Message: "Username or password is incorrect!"
}
The above was working perfectly until we added the below new configuration to redirect all 404 errors to a custom page for security issues. Now anybody (more specifically a hacker) who tries to call the API randomly, will be redirected to a custom 404 error page instead of the original .NET 404 page.
Web.config:
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" />
<error statusCode="404" path="404.html" responseMode="File"/>
</httpErrors>
The problem is:
BadRequest errors are not handled anymore as it was mentioned in the beginning, the custom JSON structure is not returned anymore, custom messages like "Username or password is incorrect!" are not taken into consideration, just the same simple text is always returned: Bad Request as per below screenshot:
The solution should be running on windows server 2016 IIS version 10.
How to solve the issue by keeping both working?
Update:
If I remove existingResponse="Replace", the badrequest message is returned, but the 404 custom error is not working anymore as per below screenshot
If I set errorMode="Detailed" the 404 custom error won't work anymore, and HTML description is returned for a bad request as you can see here:
I ended up using the below to solve the issue; Thus I won't mark it as the perfect answer since it didn't solve the above issue and I didn't know yet why the configuration did not work properly as excepted. So any answer that can solve the issue is more than welcomed, below is what worked as excepted for me:
Remove the httpErrors configuration from web.config
Use Global.asax file and add the below method to handle any error not handled in the API solution:
protected void Application_Error(object sender, EventArgs e)
{
try
{
try
{
Response.Filter = null;
}
catch { }
Exception serverException = Server.GetLastError();
//WebErrorHandler errorHandler = null;
//Try to log the inner Exception since that's what
//contains the 'real' error.
if (serverException.InnerException != null)
serverException = serverException.InnerException;
// Custom logging and notification for this application
//AppUtils.LogAndNotify(new WebErrorHandler(serverException));
Response.TrySkipIisCustomErrors = true;
string StockMessage =
"The Server Administrator has been notified and the error logged.<p>" +
"Please continue on by either clicking the back button or by returning to the home page.<p>";
// Handle some stock errors that may require special error pages
var HEx = serverException as HttpException;
if (HEx != null)
{
int HttpCode = HEx.GetHttpCode();
Server.ClearError();
if (HttpCode == 404) // Page Not Found
{
Response.StatusCode = 404;
//Response.Write("Page not found; You've accessed an invalid page on this Web server. " + StockMessage);
Response.Redirect("404.html");
return;
}
}
Server.ClearError();
Response.StatusCode = 500;
// generate a custom error page
Response.Write("Application Error; We're sorry, but an unhandled error occurred on the server." + StockMessage);
return;
}
catch (Exception ex)
{
Server.ClearError();
Response.TrySkipIisCustomErrors = true;
Response.StatusCode = 500;
Response.Write("Application Error Handler Failed; The application Error Handler failed with an exception.");
}
}
It worked like a charm:
user is redirected to 404.html custom page
Any 400 error is being thrown properly with the JSON format and proper message
I am evaluating ServiceStack at the moment. I am in need to create bunch of RESTful webservices. I have the initial code running, and I am quite happy with that. What I was struggling a bit was how to create a service that could consume POST (or PUT) HTTP request that has data in its body.
I've found this thread on ServiceStack forum (http://groups.google.com/group/servicestack/browse_thread/thread/693145f0c3033795) and folliwng it I've been guided to have a look at the following thread on SO (Json Format data from console application to service stack) but it was not really helpful - it described how to create a request, and not how to create a service that can consume such a HTTP request.
When I tried to pass additional data (in the HTTP message body) my servuce returned following error (HTTP 400):
<TaskResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="">
<ResponseStatus>
<ErrorCode>SerializationException</ErrorCode>
<Message>Could not deserialize 'application/xml' request using ServiceStackMVC.Task'
Error: System.Runtime.Serialization.SerializationException: Error in line 1 position 8.Expecting element 'Task' from namespace 'http://schemas.datacontract.org/2004/07/ServiceStackMVC'..
Encountered 'Element' with name 'Input', namespace ''.
at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObject(XmlDictionaryReader reader)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObject(Stream stream)
at ServiceStack.Text.XmlSerializer.DeserializeFromStream(Type type, Stream stream) in C:\src\ServiceStack.Text\src\ServiceStack.Text\XmlSerializer.cs:line 76
at ServiceStack.WebHost.Endpoints.Support.EndpointHandlerBase.CreateContentTypeRequest(IHttpRequest httpReq, Type requestType, String contentType) in C:\src\ServiceStack\src\ServiceStack\WebHost.Endpoints\Support\EndpointHandlerBase.cs:line 107</Message>
<StackTrace> at ServiceStack.WebHost.Endpoints.Support.EndpointHandlerBase.CreateContentTypeRequest(IHttpRequest httpReq, Type requestType, String contentType) in C:\src\ServiceStack\src\ServiceStack\WebHost.Endpoints\Support\EndpointHandlerBase.cs:line 115
at ServiceStack.WebHost.Endpoints.RestHandler.GetRequest(IHttpRequest httpReq, IRestPath restPath) in C:\src\ServiceStack\src\ServiceStack\WebHost.Endpoints\RestHandler.cs:line 98
at ServiceStack.WebHost.Endpoints.RestHandler.ProcessRequest(IHttpRequest httpReq, IHttpResponse httpRes, String operationName) in C:\src\ServiceStack\src\ServiceStack\WebHost.Endpoints\RestHandler.cs:line 60</StackTrace>
</ResponseStatus>
</TaskResponse>
This led me to https://github.com/ServiceStack/ServiceStack/wiki/Serialization-deserialization I thought that I will give IRequiresRequestStream a go. At the moment my code is as follows:
public class Task : IRequiresRequestStream
{
public string TaskName { get; set; }
public string bodyData { get; set; }
public override bool Equals(object obj)
{
Task task = obj as Task;
if (task == null)
return false;
return TaskName.Equals(task.TaskName);
}
public override int GetHashCode()
{
return TaskName.GetHashCode();
}
public System.IO.Stream RequestStream
{
get
{
return new MemoryStream(System.Text.Encoding.UTF8.GetBytes(bodyData));
}
set
{
if (value.Length == 0)
{
bodyData = string.Empty;
}
else
{
byte[] buffer = new byte[value.Length];
int bytesRead = value.Read(buffer, 0, (int)value.Length);
bodyData = System.Text.Encoding.UTF8.GetString(buffer);
}
}
}
}
and service itself:
public class TaskService : RestServiceBase<Task>
{
public List<Task> tasks { get; set; }
public override object OnGet(Task request)
{
if (string.IsNullOrEmpty(request.TaskName))
{
if (tasks == null || tasks.Count == 0)
return "<tasks/>";
StringBuilder sb = new StringBuilder();
sb.AppendLine("<tasks>");
foreach (Task t in tasks)
{
sb.AppendFormat(" <task id={0}><![CDATA[{2}]]><task/>{1}", t.TaskName, System.Environment.NewLine, t.bodyData);
}
sb.AppendLine("</tasks>");
return sb.ToString();
}
else
{
if (tasks.Contains(request))
{
var task = tasks.Where(t => t.TaskName == request.TaskName).SingleOrDefault();
return String.Format("<task id={0}><![CDATA[{2}]]><task/>{1}", task.TaskName, System.Environment.NewLine, task.bodyData);
}
else
return "<task/>";
}
}
public override object OnPost(Task request)
{
if (tasks.Contains( request ))
{
throw new HttpError(System.Net.HttpStatusCode.NotModified, "additional information");
}
tasks.Add(new Task() { TaskName = request.TaskName, bodyData = request.bodyData });
return null;
}
}
My routes:
Routes.Add<Task>("/tasks/{TaskName}").Add<Task>("/tasks");
It works but... as I couldn't find any similar example I would like to ask if this is the correct way of creating a service that is capable of processing POST requests that have additional informaion included in their message body. Am I doing anything wrong? Is there anything that I've missed?
It was also mentioned on the SO thread link to which I have provided, that using DTO is the preferred way to pass data to ServiceStack based service. Assuming that client needs to send a lot of data, how could we achieve that? I don't want to pass data as JSON object in the URI. Am I making any false assumption here?
Client is not going to be written in C# / .Net. Completely different technology is going to be used. This was one of the reasony why RESTful webservices.
I know returning xml as string may not be the best idea. At the moment it is just a sample code. This will be changed later on.
The most important part is if the solution provided for me is the proper way to create a webservice that can consume HTTP request that has xml data attached in its body. What I've shared with you works I am just not 100% sure that this is the best way to achieve my goal.
Edited on Thursday 8th of March, 2012:
After reading the answer and the comments I've changed my code a little bit. I was pretty sure that if I wanted to use serialization I had to use namespaces (when passing data in the HTTP message body to the service).
I've used http://localhost:53967/api/servicestack.task/xml/metadata?op=Task to get more information about the service I've created.
REST Paths:
All Verbs /tasks/{TaskName}
All Verbs /tasks
HTTP + XML:
POST /xml/asynconeway/Task HTTP/1.1
Host: localhost
Content-Type: application/xml
Content-Length: length
<Task xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ServiceStackMVC">
<AuxData>String</AuxData>
<TaskName>String</TaskName>
</Task>
What I wanted to check was if it was possible to "mix" REST URI and pass the rest of the data as an xml.
Using Fiddler, I've created following POST request:
POST http://localhost:53967/api/tasks/22
Request headers:
User-Agent: Fiddler
Host: localhost:53967
Content-Type: application/xml
Content-Length: 165
Request Body:
<Task xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ServiceStackMVC">
<AuxData>something</AuxData>
</Task>
My DTO right now is as follows:
public class Task
{
public string TaskName { get; set; }
public string AuxData { get; set; }
public override bool Equals(object obj)
{
Task task = obj as Task;
if (task == null)
return false;
return TaskName.Equals(task.TaskName);
}
public override int GetHashCode()
{
return TaskName.GetHashCode();
}
}
And my service code is:
public class TaskService : RestServiceBase<Task>
{
public List<Task> tasks { get; set; }
public override object OnGet(Task request)
{
return tasks;
}
public override object OnPost(Task request)
{
if (tasks.Contains( request ))
{
throw new HttpError(System.Net.HttpStatusCode.NotModified, "additional information");
}
tasks.Add(new Task() { TaskName = request.TaskName });
return null;
}
}
So is this the proper way of passing XML data to the service? I think I am quite happy with xml namespaces included - that makes it even easier to develop services.
Nope, returning an xml string it's not the recommended approach since any string returned gets written directly to the response stream so the service will only work with XML services and not all the other endpoints.
The ServiceStack Way
Is to keep your DTOs that you define your web services with in their own largely dependency-free assembly (I generally will only have a reference the impl and dep-free ServiceStack.Interfaces.dll). You can then re-use these DTOs with ServiceStack's generic Service Clients to get a succinct, typed, end-to-end API without any code-gen.
Different built-in Service Clients
Your C#/.NET clients only need to use the Service Clients contained in the
ServiceStack.Common NuGet package which just contains the ServiceStack.Text.dll, ServiceStack.Interfaces.dll and ServiceStack.Common.dll for full .NET and Silverlight 4/5 client builds.
ServiceStack.Common contains the following Service Clients:
JsonServiceClient - The lightweight, ubiquitous, self-describing resilient format
JsvServiceClient - Faster more compact than JSON ideal for .NET to .NET services
XmlServiceClient - For folks who like using XML (Slower than JSON/JSV)
Soap11ServiceClient / Soap12ServiceClient - If your company mandates the use of SOAP.
If you Install the ProtoBuf Format plugin you also have the option to use the ProtoBufServiceClient which is the fastest binary serializer for .NET.
Easy to swap, easy to test
The C# Service Clients share the same IServiceClient and IRestClient interfaces making it easy to swap out if you want to take advantage of a superior format. Here's an example taking advantage of this where the same Unit Test is also used as a JSON, XML, JSV and SOAP integration test.
By default out-of-the-box, ServiceStack makes all your services available via pre-defined routes in the following convention:
/api/[xml|json|html|jsv|csv]/[syncreply|asynconeway]/[servicename]
This is what the Service Clients use when you use the Send<TResponse> and SendAsync<TResponse> API methods which allows you to call your web services without having to define any custom routes, e.g:
var client = new JsonServiceClient();
var response = client.Send<TaskResponse>(new Task());
If you want you can use the Get, Post, Put, Delete API methods that allows you to specify a url so you can call web services using your custom routes, e.g:
Async API example
FilesResponse response;
client.GetAsync<FilesResponse>("files/", r => response = r, FailOnAsyncError);
Sync API example
var response = client.Get<FilesResponse>("files/README.txt");
Here are some Sync and Async API examples from the RestFiles example project.
XML and SOAP issues
Generally XML and SOAP are more strict and brittle compared to the other formats, to minimize interop issues and reduce payload bloat you should set a global XML Namespace for all your DTOs by adding an Assembly attribute in your DTO Assembly.cs file, e.g:
[assembly: ContractNamespace("http://schemas.servicestack.net/types",
ClrNamespace = "MyServiceModel.DtoTypes")]
If you want to use a different ContractNamespace than the above you will also need to also set it in the EndpointHostConfig.WsdlServiceNamespace if you wish to make use of the SOAP endpoints.
Here are some more versioning tips when developing SOAP/XML web services:
https://groups.google.com/d/msg/servicestack/04GQLsQ6YB4/ywonWgD2WeAJ
SOAP vs REST
Since SOAP routes all requests through the HTTP POST verb, if you wish to make each service available via SOAP as well you will need to create a new class per service and define custom REST-ful routes to each service as described here.
Due to the brittleness, bloated payload size and slower perf of SOAP/XML, it is recommended to use either the JSON, JSV or ProtoBuf formats/endpoints.
Request Model Binders
Another alternative to using IRequiresRequestStream is to use Request Model Binders you can define in your AppHost, e.g:
base.RequestBinders[typeof(Task)] = httpReq => ... requestDto;
C# Client Recommendation
The recommendation is to use ServiceStack's built-in service clients for C# clients although if you wish to use your own HttpClient, than still using the XmlServiceClient will come in handy as you can use Fiddler to see the exact wire-format ServiceStack expects.
I'm building a web api using WCF web api preview 6, currently I'm stuck with a little problem. I would like to have an operation handler to inject an IPrincipal to the operation in order to determine which user is making the request. I already have that Operation Handler and is already configured. But I noticed that when I decorate the operation with the WebInvoke attribute and simultaneously the operation receives an IPrincipal and other domain object, the system throws an exception telling me:
The HttpOperationHandlerFactory is unable to determine the input parameter that should be associated with the request message content for service operation 'NameOfTheOperation'. If the operation does not expect content in the request message use the HTTP GET method with the operation. Otherwise, ensure that one input parameter either has it's IsContentParameter property set to 'True' or is a type that is assignable to one of the following: HttpContent, ObjectContent1, HttpRequestMessage or HttpRequestMessage1.
I do not know what is happening here. To give you some background I'll post some of my code to let you know how am I doing things.
The operation:
[WebInvoke(UriTemplate = "", Method = "POST")]
[Authorization(Roles = "")]
public HttpResponseMessage<dto.Diagnostic> RegisterDiagnostic(dto.Diagnostic diagnostic, IPrincipal principal)
{
......
}
WCF web api knows when to inject the IPrincipal because I decorate the operation with a custom Authorization attribute.
The configuration in the Global file:
var config = new WebApiConfiguration() {EnableTestClient = true};
config.RegisterOAuthHanlder(); //this is an extension method
routes.SetDefaultHttpConfiguration(config);
routes.MapServiceRoute<MeasurementResource>("Measurement");
routes.MapServiceRoute<DiagnosticResource>("Diagnostic");
Then the RegisterOAuthHandler method adds an operation handler to the operation if it's been decorated with the custom authorization attibute. this is how it looks:
public static WebApiConfiguration RegisterOAuthHanlder(this WebApiConfiguration conf)
{
conf.AddRequestHandlers((coll, ep, desc) =>
{
var authorizeAttribute = desc.Attributes.OfType<AuthorizationAttribute>().FirstOrDefault();
if (authorizeAttribute != null)
{
coll.Add(new OAuthOperationHandler(authorizeAttribute));
}
});
return conf;
}
public static WebApiConfiguration AddRequestHandlers(
this WebApiConfiguration conf,
Action<Collection<HttpOperationHandler>, ServiceEndpoint, HttpOperationDescription> requestHandlerDelegate)
{
var old = conf.RequestHandlers;
conf.RequestHandlers = old == null ? requestHandlerDelegate : (coll, ep, desc) =>
{
old(coll, ep, desc);
};
return conf;
}
Can somebody help me with this? Thank you in advanced!
Try wrapping your Diagnostic param in ObjectContent i.e. ObjectContent<Diagnostic>. Then you will use the ReadAs() method to pull out the object.
It should work.
DotNetNuke 6 does not appear to support WebMethods due to modules being developed as user controls, not aspx pages.
What is the recommended way to route, call and return JSON from a DNN user module to a page containing that module?
It appears the best way to handle this problem is custom Httphandlers. I used the example found in Chris Hammonds Article for a baseline.
The general idea is that you need to create a custom HTTP handler:
<system.webServer>
<handlers>
<add name="DnnWebServicesGetHandler" verb="*" path="svc/*" type="Your.Namespace.Handler, YourAssembly" preCondition="integratedMode" />
</handlers>
</system.webServer>
You also need the legacy handler configuration:
<system.web>
<httpHandlers>
<add verb="*" path="svc/*" type="Your.Namespace.Handler, YourAssembly" />
</httpHandlers>
</system.web>
The handler itself is very simple. You use the request url and parameters to infer the necessary logic. In this case I used Json.Net to return JSON data to the client.
public class Handler: IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
//because we're coming into a URL that isn't being handled by DNN we need to figure out the PortalId
SetPortalId(context.Request);
HttpResponse response = context.Response;
response.ContentType = "application/json";
string localPath = context.Request.Url.LocalPath;
if (localPath.Contains("/svc/time"))
{
response.Write(JsonConvert.SerializeObject(DateTime.Now));
}
}
public bool IsReusable
{
get { return true; }
}
///<summary>
/// Set the portalid, taking the current request and locating which portal is being called based on this request.
/// </summary>
/// <param name="request">request</param>
private void SetPortalId(HttpRequest request)
{
string domainName = DotNetNuke.Common.Globals.GetDomainName(request, true);
string portalAlias = domainName.Substring(0, domainName.IndexOf("/svc"));
PortalAliasInfo pai = PortalSettings.GetPortalAliasInfo(portalAlias);
if (pai != null)
{
PortalId = pai.PortalID;
}
}
protected int PortalId { get; set; }
}
A call to http://mydnnsite/svc/time is properly handled and returns JSON containing the current time.
does anyone else have an issue of accessing session state/updating user information via this module? I got the request/response to work, and i can access DNN interface, however, when i try to get the current user, it returns null; thus making it impossible to verify access roles.
//Always returns an element with null parameters; not giving current user
var currentUser = UserController.Instance.GetCurrentUserInfo();
I just started with the WCF REST Starter Kit.
I created a simple service that return an array of an object.
Using the browser, everything works fine but when I use a WCF client, I get an ArgumentException.
I'm not using IIS and here is the code:
The contract:
[ServiceContract]
public interface IGiftService {
[WebGet(UriTemplate="gifts")]
[OperationContract]
List<Gift> GetGifts();
}
public class GiftService : IGiftService {
public List<Gift> GetGifts() {
return new List<Gift>() {
new Gift() { Name = "1", Price = 1.0 },
new Gift() { Name = "2", Price = 1.0 },
new Gift() { Name = "3", Price = 1.0 }
};
}
}
[DataContract]
public class Gift {
[DataMember]
public string Name { get; set; }
[DataMember]
public double Price { get; set; }
}
To start the service:
WebServiceHost2 host = new WebServiceHost2(
typeof(GiftService),
true,
new Uri("http://localhost:8099/tserverservice"));
host.Open();
Console.WriteLine("Running");
Console.ReadLine();
host.Close();
To start the client:
WebChannelFactory<IGiftService> factory = new WebChannelFactory<IGiftService>(
new Uri("http://localhost:8099/tserverservice"));
IGiftService service = factory.CreateChannel();
List<Gift> list = service.GetGifts();
Console.WriteLine("-> " + list.Count);
foreach (var item in list) {
Console.WriteLine("-> " + item.Name);
}
The server and the client are in the same solution and I'm using the same interface in both (to describe the service contract).
The exception says: "A property with the name 'UriTemplateMatchResults' already exists." and that is the stack trace:
Class firing the exception -> Microsoft.ServiceModel.Web.WrappedOperationSelector
Stack trace:
at System.ServiceModel.Channels.MessageProperties.UpdateProperty(String name, Object value, Boolean mustNotExist)
at System.ServiceModel.Channels.MessageProperties.Add(String name, Object property)
at System.ServiceModel.Dispatcher.WebHttpDispatchOperationSelector.SelectOperation(Message& message, Boolean& uriMatched)
at System.ServiceModel.Dispatcher.WebHttpDispatchOperationSelector.SelectOperation(Message& message)
at Microsoft.ServiceModel.Web.WrappedOperationSelector.SelectOperation(Message& message) in C:\Program Files\WCF REST Starter Kit\Microsoft.ServiceModel.Web\WrappedOperationSelector.cs:line 42
at Microsoft.VisualStudio.Diagnostics.ServiceModelSink.ServiceMethodResolver.GetOperation()
at Microsoft.VisualStudio.Diagnostics.ServiceModelSink.ServiceMethodResolver..ctor(ContractDescription contract, DispatchRuntime runtime, Message request, InstanceContext instanceContext)
What am I doing wrong?
UPDATE: I disabled the help page and the service is working now. Is it a bug?
host.EnableAutomaticHelpPage = false;
Thank you!
André Carlucci
Had the same problem, disabled the help page and it fixed it. The exception was being thrown if some REST urls were called in a sequence very quickly. It was fine when waiting between the calls.
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new WebServiceHost2(serviceType, true, baseAddresses) {EnableAutomaticHelpPage = false};
}
I had the same probem, but I wanted to see the help page so disabling it wasn't a solution for me. I found out that URITemplating in the WCF REST Toolkit is causing those problem, when it sees that it already have this template in the template tables. Basically you will only need a template when the URL to your method is different according to the requested data, after all, that's what the templates are for. I had the same URITemplates for my POST operations, so the URLs did not differ between separate queries which causes this error. then I found out that I actually didn't need any templating at all, at least for the POST operations, moreover youvannot make a POST query though the URL if your method requires a complex object to be passed as a parameter. So I removed the URITemplate named parameter from the WebInvoke attribute in the service interface, I think that solved the problem. Of course if you make GET queries to the server and rely on URITemplating you'll still have to either put up with or leave away the help page.
In my case, the problem only occurred when accessing the endpoint using a WCF channel with Visual Studio debugger integration enabled.
I worked around the issue by adding some code to remove the VS behaviour from the ChannelFactory:
var vsBehaviour = channelFactory.Endpoint.EndpointBehaviors
.FirstOrDefault(i =>
i.GetType().Namespace == "Microsoft.VisualStudio.Diagnostics.ServiceModelSink");
if (vsBehaviour != null)
{
channelFactory.Endpoint.Behaviors.Remove(vsBehaviour);
}
Apparently, there are other ways to disable WCF Visual Studio debugger integration, but they seem to be system-wide, while this solution is local.