0
votes

OK, there is lots going on here so I will try and keep my question and examples as simple as I can. With that in mind, please ask if you need any additional information or clarification on anything.

The code

I have a Web API 2 project which has a number of controllers and actions. The particular action I am having problems with is defined in the ContactController as follows:

[HttpPost]
public MyModel GetSomething(System.Nullable<System.Guid> uid)
{
    return GetMyModel(uid);
}

In case it matters, my routing is setup as follows:

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = RouteParameter.Optional }
);

Now I have another project that is required to call the above action. For calling the Web API I am using HttpClient. Note that I have lots of other actions calls which are working correctly, so this isn't a connectivity issue.

The code I am using to call the Web API method is as follows:

using (HttpClient client = GetClient())
{
    var obj = new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("uid", someGuid.ToString()) };
    var response = client.PostAsync(path, new FormUrlEncodedContent(obj)).Result;

    return response.Content.ReadAsAsync<T>().Result;
}

In this instance, path is basically:

localhost:12345/api/contact/getsomething

The problem

The PostAsync call Result (i.e. response in the above code) gives this message:

{StatusCode: 404, ReasonPhrase: 'Not Found', Version: 1.1, Content: System.Net.Http.StreamContent, Headers: { Pragma: no-cache X-SourceFiles: =?UTF-8?B?QzpcRGV2ZWxvcG1lbnRcUHJvamVjdHNcTGltYVxMaW1hIHYzXERFVlxMaW1hRGF0YVNlcnZpY2VcYXBpXHVzZXJhY2Nlc3NcZ2V0bW9kdWxlc2FjY2Vzcw==?= Cache-Control: no-cache Date: Fri, 18 May 2018 10:25:49 GMT Server: Microsoft-IIS/10.0 X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET Content-Length: 222 Content-Type: application/json; charset=utf-8 Expires: -1 }}

If I put a breakpoint inside the aciton then it doesn't fire. However, what I find strange is that when I call it, Visual Studio (2018) tells me that the specific action has a "failed request" on that specific action. So clearly it must know which method I am trying to call?

At this point I am running out of ideas on how to debug further. What am I doing wrong here?

3
Unrelated but don't block on async with .Result. You'll probably deadlock after you figure out your 404 issue. - Crowcoder
@Crowcoder: I will investigate, but I don't actually want it to be Async - musefan
Then use a synchronous API like HttpWebRequest or some other. - Crowcoder

3 Answers

0
votes

in this case you can use the same endpoint as for getting and posting.

so you probably need:

[HttpGet]
public IActionResult Get(System.Nullable<System.Guid> uid)
{
    return GetMyModel(uid); //make sure you got it, oterhwise return a NotFound()
}

[HttpPost]
public IActionResult Post(InputModel model)
{
    _service.doMagicStuff();
    return Ok();
}

cheers!

0
votes

not sure but error may be because of you are passing keyvalue pair

var obj = new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("uid", someGuid.ToString()) };
var response = client.PostAsync(path, new FormUrlEncodedContent(obj)).Result;

instead of guild only i.e. only string value expected by function , so it will be

var response = client.PostAsync(path, new FormUrlEncodedContent(someGuid.ToString())).Result;

method should be

[HttpPost]
public MyModel GetSomething(string uid)
{
    return GetMyModel(Guid.Parse( uid));
}
0
votes

You are sending the guid with the FormUrlEncodedContent but the requests content type is application/json. I recommend you to send it as a json like this

using (HttpClient client = GetClient())
{
    var obj = new { uid = someGuid.ToString()) };
    var json = JsonConvert.SerializeObject(obj);
    var content = new StringContent(json, Encoding.UTF8, "application/json");
    var result = client.PostAsync(path, content).Result;

    return response.Content.ReadAsAsync<T>().Result;
}

Then in the api controller, use the FromBody attribute to declare that the parameter is read from the request body

[HttpPost]
public MyModel GetSomething([FromBody]RequestModel model)
{
    return GetMyModel(model.uid);
}

public class RequestModel
{
    public System.Nullable<System.Guid> uid { get; set; }
}

Also, if you only have one Post method in the contact controller the url localhost:12345/api/contact will be enough