0

I have an ASP.NET MVC application which is invoking a ASP.NET Web Api REST service:

public class MyClass
{
    private static HttpClient client = new HttpClient();

    public async Task DumpWarehouseDataIntoFile(Warehouse myData, string path, string filename)  // See Warehouse class later in this post
    { 
        try
        {

           //Hosted web API REST Service base url  
           string Baseurl = "http://XXX.XXX.XX.X:YYYY/";  

           //using (var client = new HttpClient())  --> I have declared client as an static variable
           //{  
            //Passing service base url  
            client.BaseAddress = new Uri(Baseurl);  

            client.DefaultRequestHeaders.Clear();  
            //Define request data format  
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));  
              
            // Serialize parameter to pass to the asp web api rest service
            string jsonParam = Newtonsoft.JsonConvert.SerializeObject(myData);

            //Sending request to find web api REST service resource  using HttpClient

            var httpContent = new StringContent(jsonParam, Encoding.UTF8, "application/json");

            HttpResponseMessage Res = await client.PostAsync("api/Warehouse/DumpIntoFile", httpContent);  

            //Checking the response is successful or not which is sent using HttpClient  
            if (Res.IsSuccessStatusCode)  
            {  
               // Some other sftuff here
            }
           //}
         }
         catch (Exception ex)
         {
              // Do some stuff here
         } // End Try

    } // End DumpWarehouseDataIntoFile method
} // End class

The ASP.NET Web API REST Controller:

public class WarehouseController : ApiController
{ 
    public bool DumpIntoFile(string data)
    {
           // Stuff here
    }
}

I have used an HttpClient static variable, I am not enclosing it within a 'using' block because of it is not recommended as explained in this link: YOU'RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE

When this code is executed it fails with the error:

System.Net.Http.HttpRequestException: An error occurred while sending the request. System.Net.WebException: The remote name could not be resolved: 'xxx'

I think I do not need to dispose HttpClient client object, right?

How can I solve this?

I am using Visual Studio 2013 and NET 4.5 (Not Core)

UPDATE 2020/12/10: Previous error was occurring because of ASP.NET Web API REST Url was not configured correctly in the internal DNS.

Now after solving previous error I get below one:

404 Not Found

Below some messages of the error returned by client.PostAsync:

enter image description here

Ping to ASP.NET Web API Rest is working.

Maybe it is something missing in my web api routes configuration under App_Start\WebApiConfig.cs below?

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Configuración y servicios de API web

        // Rutas de API web
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}
12
  • 2
    "Remote name could not be resolved" sounds more like the API is not reachable. Can you Ping the server or try through cUrl / Postman? Commented Dec 7, 2020 at 10:14
  • 2
    I concur with Fildor regarding the exception. Verify that the remote API is reachable and double check your request to make sure the address is correct. Also, I recommend you take a look at using the HttpClient Factory to create your HttpClient objects. Docs link : learn.microsoft.com/en-us/aspnet/core/fundamentals/… Commented Dec 7, 2020 at 10:25
  • 2
    You should use HttpClientFactory. HttpClient caches socket connections so if the DNS entry changes it will keep trying to connect to the wrong server. DNS resolution can take up to 15 sec, so this is a huge gain. HttpClientFactory recycles connections periodically (every 10 min by default) so it will eventually pick up any DNS changes without losing the caching benefit. Commented Dec 7, 2020 at 10:40
  • 1
    Try changing the DumpWarehouseDataIntoFile() return type to Task and await it to finish? Commented Dec 7, 2020 at 11:46
  • @jandrew Is HttpClient Factory available for standard ASP.NET (Not Core)? Could you post a little code snippet on how to adapt my little code to apply HttpClient Factory? Any code snippet will be highly appreciated. I have never worked with HttpClient Factory and I think it is a good practice to use it. So If you provide some little code on how to adapt my code to it I will highly appreciate you. Thanks in advance. Commented Dec 10, 2020 at 13:55

2 Answers 2

1

I will assume you're using Web API 2 based on the WebApiConfig that you provided. In regards to the 404 response you're receiving, this stems entirely from the incorrect use of, or lack thereof, routing in your controller actions.

Using your example WebApiConfig, this would enable attribute routing as well as convention-based routing using the default route template for a Web API. The default template will not match according to action name, it only matches according to the controller name and the HTTP method of the request (GET, POST, etc.). Example GET http://localhost:5000/api/books/2 will route to the BooksController and will invoke the action method that matches the naming convention i.e. GetBookById(int id).

config.MapHttpAttributeRoutes();

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

In your case, you have an action method called DumpIntoFile(string data) which doesn't follow the standard naming convention above, therefore the routing system does not know which action to invoke. You can fix this by using the attribute routing that you enabled in the config. You need to add an attribute to your action method that specifies the HTTP verb as well as the route which should invoke the method. Note the additional attribute that should be added to the controller class, which specifies a route prefix.

[RoutePrefix("api/warehouse")]
public class WarehouseController : ApiController
{
    // Routes POST http://localhost:5000/api/warehouse/dump
    [Route("dump")]
    [HttpPost]
    public bool DumpIntoFile(string data)
    {
        //...
    }
}

This will route a POST request to http://localhost:5000/api/warehouse/dump. Obviously the localhost:5000 is a placeholder for whatever your base uri would be. I don't know how complex your data in the request is but you can reference the docs regarding how to deal with the data in your request body. The example is primarily focused on the routing portion.

This is just a quick example, there are various different attributes that you could use to specify the routes, accepted methods and so on. The docs outline it pretty well and provide plenty of examples. Link is provided below. On an additional note, I'd suggest returning one of the IHttpActionResult or HttpResponseMessage types instead of a simple bool/string/int/etc. It just gives you more options for the type of HTTP response that you're returning, these are also in the docs.

Docs: https://learn.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

Sign up to request clarification or add additional context in comments.

8 Comments

And instead of indicating the attribute RoutePrefix for controller class and attribute Route and verb httpPost for action method DumpIntoFile, is it the same as indicating below configuration in webapiconfig.cs file? config.Routes.MapHttpRoute( name: "WarehouseController", routeTemplate: "api/{controller}/{action}/{data}" ); without the need of specifying those attributes and verd in controller and action method respectively?
I am using ASP.NET Framework 4.5 and Web API 2.2 version 5.2.3. I have provided and screenshot of the nuget packages installed in it. See updated post.
Regarding to the data passed as a string in the action method DumpIntoFile, it is a class object: public class Warehouse { public DataTable dt { get; set; } public string Filepath { get; set; } } previous to make the request client.PostAsync("api/Warehouse/DumpIntoFile", httpContent); it is serialized using Newtonsoft.JsonConvert.SerializeObject(myData); then it is encapsulated into an httpContent as application/json type. Web api method DumpIntoFile receives it as string and deserializes it: JsonConvert.DeserializeObject<Warehouse>(data);
@Ralph Regarding the RoutePrefix attrribute, yes I'm fairly sure you need to include that along with the HttpPost and the Route attributes on the action method. It will use one or the other to my understanding, either attribute based or convention based. Not a mix of the two. As far as your data structure, you need to take in a parameter that matches your data structure, i.e. DumpIntoFile(Warehouse data) or whatever the specific object is that you're passing in through your request body. You can probably get away with using DeserializeObject<Warehouse> as you mentioned, just depends.
@Ralph I would suggest referencing the docs for more specific information. I would suggest using attribute routing myself, as it makes it simpler and gives you more flexibility for defining your controller action routes. learn.microsoft.com/en-us/aspnet/web-api/overview/…
|
1

Possible points to look:

  1. Check your WebApi documents. 404 in web API means, either host is not reachable or the endpoint your are calling is wrong. Check that first by pasting the URL in your browser. If its a POST operation and accessible by your machine, then you will get a method not supported error.

  2. If in the browser, if its 404 again but you know its correct then you should look into the ACL (User Authorization and Authentication) of the machine. Whether that machine is reachable/accessible to your network or not.

  3. I can't see any authorization headers in your code. Please check for that as well.

  4. Rarely possible - If it requires some pre-configured app like Facebook, Google etc.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.