I'm developing some RESTful services in WCF 4.0. I've got a method as below:
[OperationContract]
[WebGet(UriTemplate = "Test?format=XML&records={records}", ResponseFormat=WebMessageFormat.Xml)]
public string TestXml(string records)
{
return "Hello XML";
}
So if i navigate my browser to http://localhost:8000/Service/Test?format=XML&records=10, then everything works as exepcted.
HOWEVER, i want to be able to navigate to http://localhost:8000/Service/Test?format=XML and leave off the "&records=10" portion of the URL. But now, I get a service error since the URI doesn't match the expected URI template.
So how do I implement defaults for some of my query string parameters? I want to default the "records" to 10 for instance if that part is left off the query string.
Note: This question is out of date, please see the other answers.
This does not appear to be supported.
However, Microsoft has been made aware of this issue and there is a work-around:
You can get the desired effect by
omitting the Query string from the
UriTemplate on your WebGet or
WebInvoke attribute, and using
WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters
from within your handlers to inspect,
set defaults, etc. on the query
parameters.
https://connect.microsoft.com/VisualStudio/feedback/details/451296/
According to this answer this is fixed in .NET 4.0. Failing to supply the query string parameter seems to result in its being given the default value for the type.
Check this blog post out. Makes sense to me, and comes with a class to parse out the query string parameters.
http://blogs.msdn.com/b/rjacobs/archive/2009/02/10/ambiguous-uritemplates-query-parameters-and-integration-testing.aspx
Basically don't define the query string parameters in the UriTemplate so it matches with/without the parameters, and use the sample class to retrieve them if they're there in the method implementation.
This seems to work in WCF 4.0.
Just make sure to set your default value in your "Service1.svc.cs"
public string TestXml(string records)
{
if (records == null)
records = "10";
//... rest of the code
}
While this is an old question, we still come to this scenario from time to time in recent projects.
To send optional query parameters, I created WCF Web Extensions nuget package.
After installation, you can use the package like this:
using (var factory = new WebChannelFactory<IQueryParametersTestService>(new WebHttpBinding()))
{
factory.Endpoint.Address = new EndpointAddress(ServiceUri);
factory.Endpoint.EndpointBehaviors.Add(new QueryParametersServiceBehavior());
using (var client = factory.CreateWebChannel())
{
client.AddQueryParameter("format", "xml");
client.AddQueryParameter("version", "2");
var result = client.Channel.GetReport();
}
}
Server side you can retrieve the parameters using WebOperationContext:
WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters;
Related
I have the following controller in ASP.NET WebApi 2:
[RoutePrefix("Validations")]
public partial class ValidationsController
{
[HttpPost, Route("Bsb")]
public IHttpActionResult ValidateBsb(string value)
{
var validator = new BankStateBranchValidator(DbContext.BankStateBranches);
var data = new ValidationsResult
{
IsValid = validator.IsValid(value ?? string.Empty)
};
data.Error = data.IsValid
? null
: "The BSB you have entered does not appear to be valid. Please check the value and try again.";
return Ok(data);
}
}
For historical reasons, the value parameter needs to be in the querystring, rather than the form body, which should be empty. So the expected API call would be POST /Validate/Bsb?value=012345.
That all works fine, and I get the expected result; however, sometimes we are getting clients calling the API with POST /Validate/Bsb or POST /Validate/Bsb?value=, and that is resulting in a 400 Bad Request response from WebAPI itself, because, as far as I can tell, the model binder is failing to bind the missing value to the parameter. If I put a breakpoint inside the method, it never gets hit.
So, given that I can't change the API contract, how can I handle this scenario? I've tried adding a [ValueProvider(typeof(RouteDataValueProviderFactory))] attribute to the parameter, and my test case for the missing value works, but then the valid value test cases break since the value isn't in the route but in the querystring.
Update
Based on Craig H's suggestion, I've added a default value to the value parameter. So the various scenarios are:
POST /Validate/Bsb?value=012345 - pass (valid value)
POST /Validate/Bsb?value=000000 - pass (invalid value)
POST /Validate/Bsb?value= - fail (empty value)
POST /Validate/Bsb - pass (missing value)
You should be able to make the parameter optional by specifying a default value in the method signature.
e.g.
[HttpPost, Route("Bsb")]
public IHttpActionResult ValidateBsb(string value = null)
Your question says that a query with ?value= was throwing a bad request.
When I tried this locally my breakpoint was hit and value was null.
If I omitted the QS parameter completely, then I received a method not allowed response.
This page makes mention of optional route parameters with attribute routing, although you are not specifying the parameter like that here.
I cannot find the document which describes the other options with regards to routing and optional parameters. I have seen one which indicates the differences between defining it as optional in the route definition, and optional in the method signature. If I find it, I will update this answer!
I have this action method available to OData:
[HttpPost]
[ODataRoute("({id})/Replace")]
public Blog Replace([FromODataUri] int id, Blog blog)
This responds for POST requests on /odata/Blogs(1)/Replace.
It is working except for the part that the blog parameter is never bound from the POST, that is, it is not null, but has default values for all properties. If I add [FromBody] to the parameter, it becomes null. I also tried with a parameter of type ODataActionParameters, but it is always null.
This is the relevant part of my model:
var replace = builder.EntitySet<Blog>("Blogs").EntityType.Action("Replace");
//replace.Parameter<int>("id").Required(); //this can be added or not, it doesn't matter
replace.EntityParameter<Blog>("blog").Required();
replace.Returns<int>();
I read somewhere that an OData action cannot have two parameters, but I am not sure of that. I need the id parameter to be present, so I need to find a solution for this.
I am using ASP.NET Core 3.1 and the latest versions of all packages.
What am I doing wrong?
The solution turned out to be simple:
The parameter declaration was wrong: the name of the entityset is “blogs”, not “blog”
I was posting the JSON for the “Blog” entity, but I had to modify it so as to be included in a parameter “blog”, as this:
{
“blog”: {
“BlogId”: 1,
<< rest of the Blog properties >>
}
}
This solved my problem.
I am using Json.NET to serialize an object to be sent to a compact framework 3.5 device (lucky me).
My class on the compact device is:
public class Specification
{
public Guid Id { get; set; }
public String Name { get; set; }
public String Instructions { get; set; }
}
The Json being returned sent to the device is (notice the null for instructions):
string json = #"{""Name"":""Test"",""Instructions"":null,""Id"":""093a886b-8ed4-48f0-abac-013f917cfd6a""}";
...and the method being used to deserialize the json is...
var result = JsonConvert.DeserializeObject<Specification>(json);
On the server I'm using the following to create the Json:
var serializer = new JsonSerializer();
serializer.Formatting = Formatting.None;
serializer.Serialize(context.HttpContext.Response.Output, this.Data);
The problem is that on the compact framework, it's failing to put a value into result.Instructions which is causing a null reference when it is referenced later in the code.
I'm using Newtonsoft.Json.Compact v3.5.0.0 (I think that's the latest version), and on the server I'm using Newtonsoft.Json 4.5.0.0.
Question
How can I either:
a) Change the server code to stick a "" instead of a null value in where a string is null.
or
b) Change the compact framework code to be able to handle null value strings.
Things I've tried
I've been looking through the documentation/examples of Json.Net and have tried a multitude of things like a implementing a DefaultContractResolver, and a custom JsonContract. Maybe the answer lies within those but my lack of understanding of Json.Net at this level isn't helping!!
Further info
I was using System.Runtime.Serialization.Json.DataContractJsonSerializer for the server side serialisation, which did generated quotes in the event of empty strings. Unfortunately, I need more flexibility with the serialization which is why I've started using Json.Net.
Any hints/tips appreciated.
OK - no answers, but having search all yesterday afternoon I went to bed, and search again this morning to find: Serializing null in JSON.NET, which pretty much answers my question.
Here's what I want to do :
string[] names= {"Dave","Mike","Sara","Roger"};
var events = eventsDB.where(a=>names.Contains(a.name));
I think that's pretty clear. Where eventsDB is a Linq to SQL connection, all is well - But this won't work with a service. Is there any way to achieve this?
Cheers!
Thanks for the tips guys, I think I've found a way to get what I want from the web service without having to add another method to the service by simply building the query URI manually ... partly.
string[] names = { "Dave", "Mike", "Sara", "Roger" };
StringBuilder sb = new StringBuilder();
sb.Append("(");
foreach (string s in names)
{
sb.Append(String.Format("name eq '{0}'",s));
sb.Append(" or ");
}
sb.Remove(sb.Length - 4, 4);
sb.Append(")");
var events = eventsDB.AddQueryOption("$filter",sb.toString());
notice the AddQueryOption function
You could expose a service method which returns an array.
Then you could call it like
serviceProxy.serviceMethod.where(....
But I don't think you would want to so this. For a start, a database and a service are completely different things.
Linq to WCF? Nice idea but does not compute.
OData the underlying protocol for WCF Data Services currently doesn't support this.
You could implement a service operation to do this on the server side and pass in the list of values as a parameter. Note that for service operation the list would have to be concatenated to a single string, so for example comma separated list. If you would go with action instead you can pass in the list directly.
The other option is to construct a query similar to that. If the number of values in the array is small you can construct a query like
Where(a => a.Name == "John" || a.Name == "Mary" || ...)
I have a simple DataContract structured in this way:
[DataMember(EmitDefaultValue = false, IsRequired = true, Name = "TablePath", Order = 1)]
public string TablePath { get; set; }
The Value that I try to insert is something like that:
%PATH%\%SPECIAL%\file.txt
And I receive this message using the WCF Test Client UI
"... is not a valid value for this type."
I tried different combinations of characters, and for example something like this works great "%PATH%".
The problem is if there is a combination like "%\".
How I can fix this problem as I will receive any type of characters inside this property?
Thank you in advance.
Did you try adding the # sign before the string #"%PATH%\%SPECIAL%\file.txt" ?
Without it I think you need to use "\ \" instead of "\" .....
It seems that the problem is only with the WCF Test Client tool. If I use the same metadata through C# it works ... Weird as I don't have a UI now to test my services ... I must work with TDD.