Dynamic WebAPI Url - c#

I am using the following code to get data from my api:
using (var client = new HttpClient())
{
try
{
string url = "http://localhost:58639/api/cars/" + carId + "?includeOwners=true";
var model = client
.GetAsync(url)
.Result
.Content.ReadAsAsync<Car[]>().Result;
Car c = model[0];
newCount = c.Persons.Count;
/* More Code */
}
}
using this route in WebApiConfig:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Is there a way to create the URL dynamically such that if the site was hosted somewhere other than localhost this would still work? I have attempted to do this with Url.RouteUrl and UrlHelper, yet I can't find a way to include the "?includeOwners=true" filter with them.

One way is to put the server part in a config file (web.config etc) and read it from there when you're constructing the url.
Just add the base url part in an appSetting and read it using ConfigurationManager.AppSettings["NameOfKey"]

Please try the below code:
string domainName = ((System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"]).Request.ServerVariables["HTTP_HOST"];
you can dynamically load the Host and Port or the whole domainname.

Related

Error in calling web API in asp .net web forms status code 404

I have created web API that is working fine as i checked that, but the problem is when i call that web API in asp .net website here is a code below of calling web API
protected void btn_search_Click(object sender, EventArgs e)
{
HClient.BaseAddress = new Uri("http://localhost:50653/");
HClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
BoiMember obj = new BoiMember();
obj.CustomerId = txt_customerid.Text.Trim();
obj.AadhaarNo = txt_aadharno.Text.Trim();
obj.CustomerName = txt_name.Text.Trim();
obj.AccountNo = txt_accountno.Text.Trim();
obj.MobileNo = txt_mobile.Text.Trim();
obj.branchcd = Session["BranchCode"].ToString();
obj.ZoneCode = Session["ZoneCode"].ToString();
obj.Campcd = "1";
obj.ind = 1;
obj.SourceType = 2;
obj.UserId = Session["UserName"].ToString();
string uri = "api/BoiMember/GetRecord/";
var response = HClient.GetAsync(uri+obj).Result;
if (response.IsSuccessStatusCode)
{
var GetData = response.Content.ReadAsAsync<IEnumerable<BoiMember>>().Result;
GvdRecords.DataSource = GetData;
GvdRecords.DataBind();
}
else
{
}
}
Where in the API controller named BoiMemberController when I call this web API without parameters it works fine but as I Pass parameters I get the status code 404 Error not found. My web APIConfig.cs has a code
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
By default when you do [some object].ToString() the ToString method returns the objects type. So you are probably passing a string similar to api/BoiMember/GetRecord/BoiMember (not sure on the fully qualified type). You need to build up the uri using string formatting. Here is a basic example with 2 parameters:
var uri = string.Format("api/BoiMember/GetRecord/?customerId={0}&aadhaarNo={1}"
, txt_customerid.Text.Trim()
, txt_aadharno.Text.Trim());
This assumes that your parameters are query string parameters. If you have a web api with the parameter positioned inside the url then you need to change the structure of the string accordingly.
You should also have null checks if necessary and also if a parameter if empty you might not want to send it to the api.

Accessing Method in WebAPI via Put Request

My apologies if the question seems trivial but being new to WebAPI i cannot
get the client access Put Methods.
Here is my WebAPI Method
public class TestController : ApiController
{
[HttpPut]
public HttpResponseMessage Put(string id)
{
var response = new HttpResponseMessage();
response.Headers.Add("Message","Success");
return response;
}
}
Route Configuration
//configure the webapi so that it can self host
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{Controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional }
);
Client
var baseAddress="http://localhost:8000/";
var client= new HttpClient {BaseAddress = new Uri(baseAddress)};
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.PutAsJsonAsync(baseAddress+"Test", "TestParam").Result;
Console.WriteLine(response.Content);
When i run the above code.
I get 404 Error for some reason meaning the client cannot contact the webapi method.
I have three questions.
1. How can i make sure the client can correctly call the web api method.
2. Is it possible to invoke the method directly from url?
I tried http://localhost:8000/test/Dummy but no luck.
3. If its possible to invoke the method how can i make sure the message gets displayed on the page saying "Success".
Your code would work if you didn't specify the action in the configured route. In that case the action is selected by the HTTP method (PUT, POST, DELETE, GET...). The route shpukd look like this:
routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
As you've specified the {action} part in the route, you must specify the action in the url. In that case, the url should be like this:
http://localhost:8000/Test/Put
Please, see Routing in ASP.NET Web API.
If you want to test your Web API methods one of the best tools available is Postman, which is a Chrome extenion. This solves your questions 1 & 2.
For Q3: as you're making an asycn call, you have to await for the response message, read it, and show the corresponding message. You can read this documentation if you don't know the async/await model.

Creating WebAPI to allow a string to be passed with '/' characters

I'm creating a WebApi in ASP.NET. I want one of my get methods to allow a string to be passed into it. This string will function as a path to a branch in TFS where I'll do a QueryHistory to return its history.
In my WebApiConfig.cs file:
config.Routes.MapHttpRoute(
name: "Branches",
routeTemplate: "api/branches/{fullPath}",
defaults: new { controller = "branches", fullPath = RouteParameter.Optional }
);
In my controller.cs file:
// GET api/branches/$/Project/Branches/Path/To-This-Branch
public string Get(Uri fullPath)
{
string output = "";
NetworkCredential cre = new NetworkCredential("COSMO\\pd-srv", pWord);
TfsTeamProjectCollection tfs = new TfsTeamProjectCollection(new Uri("http://fortknox:8080/tfs/PD"), cre);
var service = tfs.GetService<VersionControlServer>();
string s = System.Uri.UnescapeDataString(fullPath);
var latestChange = service.QueryHistory(s, RecursionType.None, 1);
//output = JsonConvert.SerializeObject(latestChange);
//return a json formatted string containing the full history of the branch path passed in
return "value";
}
I've tried multiple ways, just seems like when I pass in a uriEncoded parameter the UnescapeDataString only accepts strings so it doesn't work. If I pass in the string I get an error due to the '/'.
How can I pass this path from my javascript to my Get() call in my api?
I have run into this as well in the past and solved it by converting "/" to "!" and then converting back in the API. You could substitute any character(s) there... you just need to get rid of the "/"
You can use {*fullPath} in the end of your route template to match the fullPath using a wildcard expression.
config.Routes.MapHttpRoute(
name: "Branches",
routeTemplate: "api/branches/{*fullPath}",
defaults: new { controller = "branches", fullPath = RouteParameter.Optional }
);
See MSDN: Handling a Variable Number of Segments in a URL Pattern
Try call Get method:
api/branches?fullPath="directory1/directory2"

Unit Testing WebApi Controller Using In-Memory HttpServer - No HTTP resource was found

From this blog article by Yusef: http://blogs.msdn.com/b/youssefm/archive/2013/01/28/writing-tests-for-an-asp-net-webapi-service.aspx
I'm trying to set up some unit test for a WebApi project but continue to get:
"No HTTP resrouce was found that matches the request URI http://localhost/api/Filter"
Test case:
[TestMethod]
public void TestMethod1()
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
HttpServer server = new HttpServer(config);
using (HttpMessageInvoker client = new HttpMessageInvoker(server))
{
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/Filter"))
{
request.Content = new StringContent(ValidJSONRequest);
request.Content.Headers.Add("content", "application/json");
using (HttpResponseMessage response = client.SendAsync(request, CancellationToken.None).Result)
{
Assert.AreEqual(ValidJSONResponse, response.Content.ReadAsStringAsync().Result);
}
}
};
}
NB. ValidJSONRequest/ValidJSONResponse are string containing JSON objects.
Running in IIS express this routing works perfectly and behaves as expected and I can't for the life of me work out what's going on? What am I missing?
Right, I'm still not sure exactly what's going on here but I've found a workaround.
This blog article contains some details - effectively the controllers context needs to be loaded up into memory... http://www.tugberkugurlu.com/archive/challenge-of-solving-an-asp-net-web-api-self-hosting-problem-no-http-resource-was-found-that-matches-the-request-uri
So how to fix it? Add this test case to the test class and it works fine.
[TestMethod]
public void Filter_Test()
{
FilterController controller = new FilterController();
}
The problem is that you're not specifying an id on your tested URL (http://localhost/api/Filter), and the configureed route doesn't have the id configured as optional.
So, either test a ULR that specifies an id, like http://localhost/api/Filter/1, or chagne the route configuration so that the id is optional, like this: instead of
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional } // optional id
);
In this way, the tested url will match the DefaultApi route.
Of course, you need a Postxxx method in your controller, because you're trying a POST action, and not specifying an action name neither in the tested URL, nor in the route definition. But, if you say it's working on local IIS, then this method must exist.

Get Controller-instances from Routes.MapHttpRoute

In an existing C# Web project here at my Job I've added a Web API part.
In four of my own classes that I use for the Web API I need to access some of the existing Controller-classes. Right now I just create a new Instance of them and everything works as intented: ProductController controller = new ProductController();
Still, creating a new ProductController while one should already exist obviously isn't a good practice. I know the Controllers are created in the Config-file in the Routes.MapHttpRoute, since it's using the C# Web MVC method. Below I've copied that piece of code:
config.Routes.MapHttpRoute(
name: "Default",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "MyProject.Controllers" }
);
route.DataTokens["UseNamespaceFallback"] = false;
I've tried to access these Controllers in my one of my API-classes like so:
private void getControllerInstance()
{
var url = "~/Products";
// Original path is stored and will be rewritten in the end
var httpContext = new HttpContextWrapper(HttpContext.Current);
string originalPath = httpContext.Request.Path;
try
{
// Fake a request to the supplied URL into the routing system
httpContext.RewritePath(url);
RouteData urlRouteData = RouteTable.Routes.GetRouteData(httpContext);
// If the route data was not found (e.g url leads to another site) then authorization is denied.
// If you want to have a navigation to a different site, don't use AuthorizationMenu
if (urlRouteData != null)
{
string controllerName = urlRouteData.Values["controller"].ToString();
// Get an instance of the controller that would handle this route
var requestContext = new RequestContext(httpContext, urlRouteData);
var controllerFactory = ControllerBuilder.Current.GetControllerFactory();
// TODO: Fix error (The controller for path '/Products' was not found or does not implement IController.) on this line:
var controllerbase = (ControllerBase)controllerFactory.CreateController(requestContext, controllerName);
controller = (ProductController)controllerbase;
}
}
finally
{
// Reset our request path.
httpContext.RewritePath(originalPath);
}
}
As you might have noticed by the TODO-comment, at the line var controllerbase = (ControllerBase)controllerFactory.CreateController(requestContext, controllerName);, I get the following error:
HttpException was unhandler by user code: The controller for path '/Products' was not found or does not implement IController.
Does anyone know how to fix this error? Has this got something to do with one of the following two lines of the code in the Config-file?
namespaces: new[] { "MyProject.Controllers" }
route.DataTokens["UseNamespaceFallback"] = false;
Or did I do something else wrong?
A tip to everyone: Don't continue programming when you are very, very tired.. Anyway, everything was correct except for a small flaw:
My API Controller is called ProductsController and my normal (default) controller is called ProductController. In the method above I use:
var url = "~/Products";
To access the ProductController..
So, after removing the "s" (and for good measure make everything lower case) I have the following instead:
var url = "~/product";
And now it works..

Categories

Resources