Pass null String to Web API 2 (Visual Studio 2013) - asp.net-web-api

I have a method like this:
public IEnumerable<Test> GetTest(Int32 idTest, String codTest, Int value)
and a test client like this:
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
response = await client.GetAsync("Test/GetTest/0/null/1/");
I need to pass null in the second parameter (that is not optional), but on server side I get a string with "null" inside and not the null value. I would not to put conversion functions for each parameter. I see that works if I put [FromBody] attribute to just one parameter, but if I set [FromBody] for all the string parameters I get internal server error.
Many thanks.

You typically shouldn't have optional parameters in the middle of a route.
I would recommend changing the order of your parameters, or add them as query parameters so they can be optional.
e.g. for http://example.com/Test/GetTest?a=0&b=&c=1
public IEnumerable<string> GetTest(Int32 idTest)
{
var queryPairs = Request.GetQueryNameValuePairs();
DoSomething(queryPairs);
...
}

Related

How to get Spring Boot to map query parameters separately from form data?

#RequestMapping(value = "/**", consumes = MediaType.ALL_VALUE)
#ResponseBody
public Object mirror(HttpServletRequest req, #Nullable #RequestBody Map<String, String> form) {
...
}
I just want the plain key and value for all form data entries here but it also includes query parameters in the map.
I need to be able to tell the difference between what came from the form and what came from the query.
Getting the query parameters separately is easily done using URI parsing but it's not so easy to remove the query parameters from the form map. Especially in the case they have the same keys.
Changing the parameter to MultiValueMap adds values with the same key into an array. Using just a Map causes the query parameters to overwrite the form data with equal keys.
I found where this is happening, for the MockHttpServletRequest at least: buildRequest method:
String query = this.url.getRawQuery();
if (!this.queryParams.isEmpty()) {
String s = UriComponentsBuilder.newInstance().queryParams(this.queryParams).build().encode().getQuery();
query = StringUtils.isEmpty(query) ? s : query + "&" + s;
}
if (query != null) {
request.setQueryString(query);
}
addRequestParams(request, UriComponentsBuilder.fromUri(this.url).build().getQueryParams());
this.parameters.forEach((name, values) -> {
for (String value : values) {
request.addParameter(name, value);
}
});
It's combining the form data and query data into one map. So is there an alternative way to parse the form data ONLY or exclude query params from the map!?
From the javadoc for #RequestParam:
In Spring MVC, "request parameters" map to query parameters, form data, and parts in multipart requests. This is because the Servlet API combines query parameters and form data into a single map called "parameters", and that includes automatic parsing of the request body.
Not sure if there's a more elegant way, but you could possibly use Spring's UriComponentsBuilder class to parse the URI string and get back the query parameters.

Response Caching using VaryByQueryKeys with dictionary querystring argument

My ASP.NET Core MVC application has a controller method with a dictionary argument, passed via the request query string:
public async Task<IActionResult> Get([FromQuery] Dictionary<string, string> filterCriteria)
{
}
The application uses Response Caching with the VaryByQueryKeys option to return cached responses for requests that have previously been served. This works as required for simple querystring values, using an attribute on the method with the following syntax: [ResponseCache(VaryByQueryKeys = new[] { "argName" }, Duration = 20)]
However, I want to use it with a dictionary argument illustrated above.
Can it be used with an object argument like a dictionary? What is the syntax to use when specifying the list of querystring keys in the ResponseCache attribute?
It turns out you can specify queryKeys for a dictionary argument using the following syntax:
[ResponseCache(VaryByQueryKeys = new[] { "argName[key1]", "argName[key2]", ... }, Duration = 20)]

Unable to cast object of type ‘System.String’ to type ‘AuthBotES.ReturnIntents’

Currently I had integrate LUIS with Bot Framework v4.
When I search for result match with Intent,
the Bot return me with this Error:
Error : Unable to cast object of type ‘System.String’ to type ‘AuthBotES.ReturnIntents’.
My Source code as below:
if (stepContext.Result != null)
{
var result = (ReturnIntents)stepContext.Result;
var msg = $"{result}";
await stepContext.Context.SendActivityAsync(MessageFactory.Text(msg), cancellationToken);
}
and my ReturnIntents classes.
public class ReturnIntents
{
public string Intent { get; set; }
public double Score { get; set; }
public string Entities { get; set; }
}
A few issues here:
The first code block you posted looks to be for handling the result of a dialog, not for processing a LUIS result.
The cast from string to ReturnIntents will always fail.
Even if your cast of stepContext.Result to ReturnIntents did work, your msg variable would only contain namespace.to.class.ReturnIntents (the string representation of the object type, not the string representation of the objects properties.
Your msg variable is redundant.
I will address these in the order that they occur.
1 - Incorrect code block
This block of code looks suspiciously like code used to process a dialog and here e.g.:
var result = (bool)stepContext.Result;
Rather than the code for handling a LUIS result e.g.:
var dispatchResult = await cognitiveModels.DispatchService.RecognizeAsync(dc.Context, CancellationToken.None);
2 - Casting error
The error is telling you that it doesn't know how to convert a string object to a ReturnIntents object. To convert the string to your object you could use a couple of methods:
Use the NewtonSoft.Json NuGet package to allow you to turn the JSON string into your object as explained here.
2) A user defined type conversion as detailed in the official docs here and explained in this answer.
This error is a red herring in terms of your solution because I believe you're accidentally copied in the wrong block of code.
3 - Incorrect ToString behaviour
To get the string value of a ReturnIntents you will need to override the ToString method for the class and write your own custom implementation.
4 - Redundant cast
This:
// We know that this cast fails, and that stepContext.Result is a string
var result = (ReturnIntents)stepContext.Result;
// This will only return <namespace.path>.ReturnIntents (if the cast above works)
var msg = $"{result}";
// Passing in message msg isn't required, we can just pass in stepContext.Result
await stepContext.Context.SendActivityAsync(MessageFactory.Text(msg), cancellationToken);
Becomes:
var result = stepContext.Result;
await stepContext.Context.SendActivityAsync(MessageFactory.Text(result), cancellationToken);
So what I actually think you actually want is the following:
var dispatchResult = await cognitiveModels.DispatchService.RecognizeAsync<ReturnIntents>(dc.Context, CancellationToken.None);
Which will send the user's input off to LUIS, and deserialize the response into a ReturnIntents object.
Edit to provide solution to the OP
The ExecuteLuisQuery method called here, and defined here returns a ReturnIntents object.
This object is passed as an option to the ReturnIntentDialog here. Because this comes through as an instance of the object type you have a few options inside your FinalStepAsync method here to turn your options object into a ReturnIntents object.:
Casting
ReturnIntents returnIntents = null;
if (stepContext.Options is ReturnIntents)
{
returnIntents = (ReturnIntents)stepContext.Options;
}
Deserializing
using Newtonsoft.Json;
ReturnIntents returnIntents = null;
if (stepContext.Options is ReturnIntents)
{
returnIntents = JsonConvert.DeserializeObject<ReturnIntents>(JsonConvert.SerializeObject(stepContext.Options));
}

Springboot #PathVariable value is including parenthesis

I am creating simple SpringBoot application. I declared PUT operation as follows
#RequestMapping(method=RequestMethod.PUT, value= "/topics/{id}")
public void updateTopic(#PathVariable String id, Topic t){
System.out.println("Kaushik==="+id);
topicService.updateTopic(Integer.parseInt(id),t);
}
When I invoke PUT operation on URL http://localhost:8080/topics/{2}. It fails.
The value of variable id is "{2}" instead of simply "2" which is causing number format exception.
I also tried specifying parameter name. public void updateTopic(#PathVariable(name="id") String id, Topic t){ but ut did not work either.
Your decratation of the PUT endpoint is perfectly fine:
#RequestMapping(method=RequestMethod.PUT, value= "/topics/{id}")
To call the URL simply use the desired value in place of {id}:
http://localhost:8080/topics/2
The URL template variable {var} is just an expression which marks part of the URL to be converted into the method parameter.

Creating a route that can accept a DateTime in the URI with asp.net web api 2 attribute routing

I'm trying to see if I need to write a custom IHttpRouteConstraint or if I can wrestle with the built-in ones to get what I want. I can't see to find any good documentation on this anywhere.
Basically, here's my action:
[Route("var/{varId:int:min(1)}/slot/{*slot:datetime}")]
public async Task<HttpResponseMessage> Put(int varId, DateTime slot)
{
...
}
What I want is to be able to call it like this:
PUT /api/data/var/1/slot/2012/01/01/131516 and have the framework bind 19 to var id and a DateTime with a value of "Jan 1st, 2012, 1:15:16pm" as the "slot" value.
Following the guide from here: http://www.asp.net/web-api/overview/web-api-routing-and-actions/create-a-rest-api-with-attribute-routing I am able to get it to work by passing in just the date segments, i.e. PUT /api/data/var/1/slot/2012/01/01 or PUT /api/data/var/1/slot/2012-01-01, but that only gives me a data value, no time components.
Something tells me that trying to pass in time in any sane way through URI segments is a bad idea, but I'm not sure why it'd be a bad idea, besides the ambiguity regarding local vs UTC times.
I've also tried constraining the datetime constraint with a regex, e.g. {slot:datetime:regex(\\d{4}/\\d{2}/\\d{2})/\\d{4})} to try to get it to parse something like 2013/01/01/151617 as a DateTime, but to no avail.
I'm pretty sure I can get this to work with a custom IHttpRouteConstraint, I just don't want to do something that might be built in.
Thanks!
an option is to pass the DateTime as query string parameters (see [FromUri]
e.g.
[Route("api/Customer/{customerId}/Calls/")]
public List<CallDto> GetCalls(int customerId, [FromUri]DateTime start, [FromUri]DateTime end)
this will have a signature of
GET api/Customer/{customerId}/Calls?start={start}&end={end}
Create the query string dates with
startDate.ToString("s", CultureInfo.InvariantCulture);
query string will look like
api/Customer/81/Calls?start=2014-07-25T00:00:00&end=2014-07-26T00:00:00
Web API datetime constraint doesn't do anything special regarding parsing datetime as you can notice below(source code here).
If your request url is like var/1/slot/2012-01-01 1:45:30 PM or var/1/slot/2012/01/01 1:45:30 PM, it seems to work fine...but I guess if you need full flexibility then creating a custom constraint is the best option...
public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
{
if (parameterName == null)
{
throw Error.ArgumentNull("parameterName");
}
if (values == null)
{
throw Error.ArgumentNull("values");
}
object value;
if (values.TryGetValue(parameterName, out value) && value != null)
{
if (value is DateTime)
{
return true;
}
DateTime result;
string valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
return DateTime.TryParse(valueString, CultureInfo.InvariantCulture, DateTimeStyles.None, out result);
}
return false;
}

Resources