RestSharp / ASP.NET WebAPI - Using POST with url parameters - c#

I've the following ASP.NET WebAPI binding:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional}
);
And my Controller looks like this:
public class ReferenceDataController : BaseController
{
[RequireUserToken(ApprovedDeviceToken = true, ValidUserToken = true)]
[HttpPost]
public IEnumerable<SynchronizeItem<IReferenceDataItem>> Sync([FromBody]IEnumerable<SynchronizeItem<IReferenceDataItem>> clientSyncItems, [FromUri]int referenceDataType)
{
// my code
}
On the client site I use the following code to send a request:
var client = new RestClient (baseUrl);
var request = new RestRequest (resource, method);
request.XmlSerializer = new JsonSerializer ();
request.RequestFormat = DataFormat.Json;
request.AddHeader ("X-Abc-DeviceToken", deviceToken);
if (!string.IsNullOrWhiteSpace (userToken))
request.AddHeader ("X-Abc-UserToken", userToken);
if (payload != null)
request.AddBody (payload);
if (parameters != null)
{
foreach (var parameter in parameters)
{
request.AddUrlSegment(parameter.Key, parameter.Value);
}
}
var response = client.Execute<T> (request);
My expectation is, sending a POST request to http://myhost/api/referencedata/sync?referencedatatype=countries with a body which contains an IEnumerable. If I remove the UrlSegment parameters on client site and the second argument on the webservice site, than it works.
How can I combine a body with payload and additional URL parameters?

You can define your action method as follow,
[RequireUserToken(ApprovedDeviceToken = true, ValidUserToken = true)]
[HttpPost]
public IEnumerable<SynchronizeItem<IReferenceDataItem>> Sync(IEnumerable<SynchronizeItem<IReferenceDataItem>> clientSyncItems, int referenceDataType)
{
// my code
}
No BodyAttribute or FromUriAttribute. In that way, Web API will try to use a MediaTypeFormatter to deserialize the body into the clientSyncItems collection and any additional value type from the query string (referenceDataType from the query string). The route as you defined it will take "sync" as Id (which would be ignored as it is not a parameter in your action).
You must also specify a content-type header so Web API can choose the right formatter (json or xml for example).

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.

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.

Dynamic WebAPI Url

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.

How do I consume a custom route using attributerouting?

I have a bunch of custom routes defined using AttributeRouting. I have a function in a controller that is trying to access one of these API functions at /api/GetBatchItems.
GetBatchItems is a function of the controller APIController, similar to:
[RouteArea("api")]
public sealed class APIController : ApiController{
[GET("GetBatches")]
public IEnumerable<PRAT.Models.EF.EFBatchItem> GetBatches() { ... }
}
In another controller, I am trying to get the result. When browsing directly everything is fine if I do it this way, but I want to be able to use my already defined route, is there a way to do that? I saw someone mention HttpAttributeRoutingConfiguration but I could not find that class anywhere. I don't want to have to use the MapHttpRoute method this way...
var config = new HttpConfiguration();
config.Routes.MapHttpRoute("default", "api/{controller}/{id}", null);
var server = new HttpServer(config);
var client = new HttpClient(server);
string url = Request.Url.GetLeftPart(UriPartial.Authority) + "/api/APIController/GetBatches";
var result = client.GetAsync(url).Result;
var content = result.Content;
var model = content.ReadAsAsync<IEnumerable<PRAT.Models.EF.EFBatchItem>>().Result;
if (model == null) return View();
else return View(model);
TO MAKE YOUR SAMPLE CODE WORK
Your existing code sample needs two changes to work:
make {id} optional, since GetBatches() has no parameter:
config.Routes.MapHttpRoute("default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
since HttpConfiguration routes match {controller} to a class named {controller}Controller, change your url calculation to:
string url = Request.Url.GetLeftPart(UriPartial.Authority) + "/api/API/GetBatches";
SIMPLER VERSION
Your can use your existing route /api/GetBatches
var client = new HttpClient();
string url = Request.Url.GetLeftPart(UriPartial.Authority) + "/api/GetBatches";
var result = client.GetAsync(url).Result;
var content = result.Content;
var model = content.ReadAsAsync<IEnumerable<MyViewModel>>().Result;
if (model == null) return View();
else return View(model);
EVEN SIMPLER (IF YOU DO NOT NEED HTTP)
Replace your sample code with this Add this extension class:
var model = (new APIController()).GetBatches();
if (model == null) return View();
else return View(model);

Categories

Resources