Web api return 404 - c#

I'm doing some automated integration test in visual studio on my web api controller. I've got the following code in my test:
var url = serverAddress + "/api/PostalCodes?postalCode=" + postalCodeToFind;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("accept", "application/json");
HttpResponseMessage response = client.GetAsync(url).Result;
response.EnsureSuccessStatusCode();
I get the following error:
System.Net.Http.HttpRequestException: Response status code does not
indicate success: 404 (Not Found).
The selfhost server used:
private const string serverAddress = "http://localhost:8080";
[TestInitialize]
public void Initialize()
{
config = new HttpSelfHostConfiguration(serverAddress);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.MaxReceivedMessageSize = 2024 * 2024;
config.MaxBufferSize = 2024 * 2024;
server = new HttpSelfHostServer(config);
server.OpenAsync().Wait();
}
The weird part is that if I have my web api controller in IIS, the same URL will work just fine and return a value. I also have the same url doing a post (without the postalCodeToFind query parameter) and it also works well...
Any clues ?

The controller is not being loaded by the test.
Do you have a custom Resolver or are you expecting the baked in resolver to pick it up?
If you're using a custom Resolver (i.e. inheriting from DefaultAssembliesResolver), make sure the library containing the controller is being loaded.
If you're expecting the baked in resolver to pick it up, the library containing your controller needs to be in the directory that the test is running from.

Related

Can't get Fiddler to capture HttpClient web api calls from MVC with localhost different port

Yet another fiddler can't get it to capture post.
Similar to this SO Post I have spent two hours now reading and trying different solution yet none of them allow me to see my fiddler web api traffic.
As a side note my code is working I am just focused on getting fiddler to show me the api calls.
I will describe my setup then what I have tried.
My Web API is a separate MVC 6, EF 7 project running on port 63381
http://localhost:63381/
My ASP.NET MVC 5 web client project is running on port: 59722
http://localhost:59722/
A typical action controller in the mvc client:
//Controller CTOR
public ClientController()
{
client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:63381/api/MyApi");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
//Action within ClientController
public async Task<JsonResult> AddNewSubCategory()
{
HttpResponseMessage responseMessage = await client.PostAsJsonAsync(url2, content);
if (responseMessage.IsSuccessStatusCode)
{
return Json("[{Update Success}]", JsonRequestBehavior.AllowGet);
}
return Json("[{Error Updating}]", JsonRequestBehavior.AllowGet);
}
}
Added the block to 32 & 62 machine.config. Restarted visual studio did NOT restart machine or any other service. This did not work.
Added the block to the client web.config and this didn't work.
Changed localhost to machinename:port
Specifically I changed http://localhost:63381/api/MyApi to http://gpgvm-pc:63381/api/MyApi
Modified Global.asax with:
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
Fiddler custom rules
Reverse proxy
Set fiddler listening on a different port.
At this point I surrender. It seems to me #1 should work to capture everything but I am obviously still doing something wrong because I can get fiddler to capture one or the other but NOT the client calling off to the client???
Update:
My machine locked and after reboot I started seeing the api calls so this issue was something with my machine. So sorry to bother.
The problem is most likely that you are using localhost which are handled in a special way.
Try using machine name or your ip instead (do not use 127.0.0.1).
The documentation have information about this as well:
http://docs.telerik.com/fiddler/Observe-Traffic/Troubleshooting/NoTrafficToLocalhost
If you try to hit specific action in api then use that code in webapi config
public static void Register(HttpConfiguration config)
{
//config.Routes.MapHttpRoute(
// name: "DefaultApi",
// routeTemplate: "api/{controller}/{id}",
// defaults: new { id = RouteParameter.Optional });
config.Routes.MapHttpRoute("API Default", "api/{controller}/{action}/{id}",
new { id = RouteParameter.Optional });
}
This code where u call your api.
public ActionResult ClientController(model content)
{
try
{
HttpClient client = new HttpClient("http://localhost:63381/");
client.BaseAddress = new Uri();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.PostAsJsonAsync("api/MyApi/url2", content).Result;
if (response.IsSuccessStatusCode)
{
return Json(new { code = 0, message = "Success" });
}
else
{
return Json(new { code = -1, message = "Failed" });
}
}
catch (Exception ex)
{
int code = -2;
return Json(new { code = code, message = "Failed" });
}
}
}

MapHttpRoute with custom HttpMessageHandler

So I'm having a troubles getting the route to work properly after the messagehandler has finished.
Error that shows up are:
No HTTP resource was found that matches the request URI
this is what I got so far:
http://localhost:51077/api/v1/project/getprojects?apikey=123456
// all actions under /project routes require authentication
config.Routes.MapHttpRoute(
"ProjectApi",
"api/v1/{controller}/{action}/{apikey}",
new { apikey = RouteParameter.Optional },
null,
HttpClientFactory.CreatePipeline(
new HttpControllerDispatcher(config),
new DelegatingHandler[]{new BasicAuthHandler(config)}));
// all routes requires an api key
config.MessageHandlers.Add(new ApiKeyHandler());
config.MapHttpAttributeRoutes();
[RoutePrefix("api/v1/ProjectController")]
public class ProjectController : BaseApiController
{
[HttpGet]
[Route("getprojects")]
public HttpResponseMessage GetProjects()
{
HttpResponseMessage resp = new HttpResponseMessage(HttpStatusCode.OK);
if(User.Identity.IsAuthenticated)
{
}
return resp;
}
}
So, all calls will first be checked if they have an ApiKey included to be able to connect (ApiKeyHandler) Then a popup appears and asks for username and password(BasicAuthHandler). If the log in is a success then it should be forwarded to the getprojects method under /project...
ApiKey is checked, username/password popup appears and is granted but then the error above comes and the route seems to be to invalid. I've tried different ways to get this to work but it seems I'm missing something here.
PROBLEM SOLVED
Just comment this line out and it works
[Route("getprojects")] // <--- COMMENT/REMOVE THIS LINE
Any ideas to why this was causing the problem?
Your issue doesn't replicate in my test that I tried at my end. It seems you have different names given to your controller in your RoutePrefix attribute. According to the request Url given:
http://localhost:51077/api/v1/project/getprojects?apikey=123456
RoutePrefix attibute on ProjectController class should look like:
[RoutePrefix("api/v1/Project")]

Send and Receive an IList<int> with ASP.NET Web Api

I need to send and receive an IList to my Web API:
API Method:
public IList<int> GetKeywordIdsFromIds([FromUri]List<int> ids)
{
// this methods retuns null or an IList<int>
return _iKeywordService.GetKeywordIdsFromIKeywordIds(ids);
}
Call method:
public List<int> GetKeywordIdsFromIds(IList<int> iKeywordIds)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(API_BASE_URL);
client.DefaultRequestHeaders.Accept.Clear();
return client.GetAsync(url + iKeywordIds).Result.Content.ReadAsAsync<List<int>>().Result;
}
}
I get the error:
Exception:Thrown: "No MediaTypeFormatter is available to read an object of type 'List`1' from content with media type 'text/html'." (System.Net.Http.UnsupportedMediaTypeException)
EDIT:
I don't know if this helps but I get this from IIS Log File:
2014-03-31 16:11:31 127.0.0.1 GET /api/ikeyword/getkeywordidsfromids/System.Collections.Generic.List`1[System.Int32] - 80 - 127.0.0.1 - 404 0 2 1
EDIT2:
Default route:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
To call the Web API action with a list of ints in the query string, do it like this...
/api/ikeyword/getkeywordidsfromids?ids=1&ids=3&ids=4
or
/api/ikeyword/getkeywordidsfromids?ids[]=1&ids[]=3&ids[]=4
From your IIS logs, it looks like you need to properly format the list in your console app and append it to the URL. Your calling code should look something like this...
var parameters = ids.Select(id => string.Format("ids={0}", id)).ToArray();
var url = string.Format("/api/ikeyword/getkeywordidsfromids?{0}", string.Join("&", parameters));

Debugging a self hosted WebApi application

I have a WebApi application with the following controllor:
public class ContentController : ApiController
{
[HttpPost]
public HttpResponseMessage Post(string contentType)
{
//do stuff
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
The route looks like this
routes.MapHttpRoute("content",
"api/content/{contentType}",
new { controller = "Content", contentType = RouteParameter.Optional });
When I host the service in IIS / cassini, if I POST to api/content/whatever then as expected, my controller action is hit.
However,
I've got a test project, that SelfHosts this api
using (var client = new HttpClient(Server))
{
var result = client.PostAsync(BaseAddress + "api/content/whatever"
var message = result.Content.ReadAsStringAsync().Result;
}
If I debug the unit test, and step into it, result is:
{StatusCode: 500, ReasonPhrase: 'Internal Server Error', Version: 1.1, Content: System.Net.Http.ObjectContent`1[[System.Web.Http.HttpError, System.Web.Http, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]], Headers:
{
Content-Type: application/json; charset=utf-8
}}
Unhelpfully, message is literally just
An error has occurred
Is there a way I can debug my self hosted WebApi to find out what is causing this error?
Set up
Server is just an HttpServer from a base class, that holds my self hosted server, new'd up like so:
var httpConfig = new HttpSelfHostConfiguration(BaseAddress);
new ApiServiceConfiguration(httpConfig).Configure();
var server = new HttpSelfHostServer(httpConfig);
server.OpenAsync().Wait();
Server = server;
If you enable tracing and add a Trace.Listeners.Add(new ConsoleTraceListener()) then you will get more detailed error messages. However, your problem is most likely related to the fact that the object that you are trying to serialize is failing to serialize.
Added to Startup
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
where config is of HttpConfiguration type.
This single line of code solved the same issue for me - now I can see all the inner exception's details.

How do you debug MVC 4 API routes?

I have a WP7 game that uses RESTsharp to communicate with my MVC4 RESTful server, but I often have issues making requests that work and therefore I want to debug where it fails.
This is an example where the Constructor on my GameController is hit, but the Post method is not hit, and I don't understand why.
Client code:
public void JoinRandomGame()
{
client = new RestClient
{
CookieContainer = new CookieContainer(),
BaseUrl = "http://localhost:21688/api/",
};
client.Authenticator = GetAuth();
RestRequest request = new RestRequest(Method.POST)
{
RequestFormat = DataFormat.Json,
Resource = "game/"
};
client.PostAsync(request, (response, ds) =>
{});
}
Server code:
public void Post(int id)
{
if (ControllerContext.Request.Headers.Authorization == null)
{
//No auth
}
if (!loginManager.VerifyLogin(ControllerContext.Request.Headers.Authorization.Parameter))
{
//Failed login
}
string username;
string password;
LoginManager.DecodeBase64(ControllerContext.Request.Headers.Authorization.Parameter, out username, out password);
gameManager.JoinRandomGame(username);
}
My routes are like this
routes.MapHttpRoute(
name: "gameAPI",
routeTemplate: "api/game/{gameId}",
defaults: new
{
controller = "game",
gameId = RouteParameter.Optional
}
);
Another way is to add an event handler in Global.asax.cs to pick up the incoming request and then look at the route values in the VS debugger. Override the Init method as follows:
public override void Init()
{
base.Init();
this.AcquireRequestState += showRouteValues;
}
protected void showRouteValues(object sender, EventArgs e)
{
var context = HttpContext.Current;
if (context == null)
return;
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(context));
}
Then set a breakpoint in showRouteValues and look at the contents of routeData.
Keep in mind that in a Web API project, the Http routes are in WebApiConfig.cs, not RouteConfig.cs.
RouteDebugger is good for figuring out which routes will/will not be hit.
http://nuget.org/packages/routedebugger
You can try ASP.NET Web API Route Debugger. It is written for Web Api. https://trocolate.wordpress.com/2013/02/06/introducing-asp-net-web-api-route-debugger/
http://nuget.org/packages/WebApiRouteDebugger/
There are more possibilities for testing the routes. You can try either manual testing or automated as part of unit tests.
Manual testing:
RouteDebugger
MVC API tracing
Automated testing:
MvcRouteTester.
Is GameController deriving from ApiController ?
Are you using WebApi ?
If not then i think the "/api/" is reserved for new WebApi feature. Try changing your routing and controller name to "gameapi"
If however you are using WebApi.
Then remove api from yor BaseUrl
client = new RestClient
{
CookieContainer = new CookieContainer(),
BaseUrl = "http://localhost:21688/",
};

Categories