Debugging a self hosted WebApi application - c#

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.

Related

Error on try to post file from Angular 6 to ASP.NET Core Web Api

I'm trying to do a POST request with multipart/form-data from an Angular 6 application to a REST service in ASP.NET Core, but I have a Error 500. I tried too many solutions, but nothing worked. I tried to execute the request on Postman and get the same problem, but I tried in other software called ARC (Advanced REST Client) and works like a charm and I don't have any idea why.
On the server-side, I getting InvalidDataException: Missing content-type boundary. In my project, I'm using swagger too
here is my code
The request in angular:
public uploadPlanilha(planilha: File, idLote: number): Observable<Array<RequisicaoComposicao>>{
let formData = new FormData();
formData.append('arquivo', planilha, planilha.name);
formData.append('idLote', idLote.toString());
let httpHeaders = new HttpHeaders();
httpHeaders = httpHeaders.set("Content-Type", "multipart/form-data");
return this.httpClient.post<Array<RequisicaoComposicao>>(`${this.API_URL}requisicoes/upload`, formData, {
headers: httpHeaders
});
}
The controller method in Web Api
[HttpPost]
[Route("upload")]
[Consumes("multipart/form-data")]
[DisableRequestSizeLimit]
public ActionResult<List<RequisicaoComposicao>> PostPlanilhaRequisicoes([FromForm]ArquivoDto arquivoDto)
{
try
{
using (Stream arquivoPlanilha = arquivoDto.Arquivo.OpenReadStream())
{
List<RequisicaoComposicao> requisicaoComposicoes = _composicaoGestor.Inserir(arquivoPlanilha, int.Parse(arquivoDto.IdLote));
return Ok(requisicaoComposicoes);
}
}
catch (Exception)
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
The ArquivoDto class
public class ArquivoDto
{
public IFormFile Arquivo { get; set; }
public string IdLote { get; set; }
}
Remove the [FromForm] tag you don't need it.
On this line here:
formData.append('arquivo', planilha, planilha.name);
change it to formData.append('arquivo', planilha);
I never used it, therefore I don't think you need the [Consumes("multipart/form-data")] attribute ether. (unless you are using something like swagger and you want to tell it what this method is consuming then keep it)
I'd also remove this line httpHeaders = httpHeaders.set("Accept", "multipart/form-data");
I use AngularJS + ASP.NET Core 2.1 and find the same error --> System.IO.InvalidDataException: Missing content-type boundary.
My solution is setting 'Content-Type: undefined' in http request front-end. I read somewhere at StackOverflow, you may give it a quick try if it helps.

C# PACT - Consumer Driven Contact testing - writing test for provider

I am trying to get my head about PACT and I am using the PACT-Net library to achieve this.
My tests on the Consumer are working fine but I am trying to setup the tests on the Provider. I am using the basic Web API project that loads when you use the Web API template within Visual Studio - which creates the Values API controller. I am just testing the Get IEumerable<string> method as an end to end test of the process. I am also following the example on the PACT-Net github site. Here is the test I have so far:
[Fact]
public void EnsureValuesReturnedFromApi()
{
var config = new PactVerifierConfig
{
Outputters = new List<IOutput>
{
new XUnitOutput(_output)
}
};
using (WebApp.Start<TestStartup>(serviceUri))
{
var pactVerifier = new PactVerifier(config);
pactVerifier.ProviderState($"{serviceUri}/provider-states")
.ServiceProvider("Values API", serviceUri)
.HonoursPactWith("Consumer")
.PactUri("http://localhost:8080/pacts/provider/Values%20API/consumer/Consumer/latest")
.Verify();
}
}
When ever I run the unit test I get the following error:
Reading pact at http://localhost:8080/pacts/provider/Values%20API/consumer/Consumer/latest
Verifying a pact between Consumer and Values API
Given When I want the values
Getting a list
with GET /api/values
returns a response which
has status code 200 (FAILED - 1)
has a matching body (FAILED - 2)
includes headers
"Accept" with value "application/json" (FAILED - 3)
"Content-Type" with value "application/json" (FAILED - 4)
Failures:
1) Verifying a pact between Consumer and Values API Given When I want the values Getting a list with GET /api/values returns a response which has status code 200
Failure/Error: set_up_provider_state interaction.provider_state, options[:consumer]
Pact::ProviderVerifier::SetUpProviderStateError:
Error setting up provider state 'When I want the values' for consumer 'Consumer' at http://localhost:9222/provider-states. response status=500 response body=
2) Verifying a pact between Consumer and Values API Given When I want the values Getting a list with GET /api/values returns a response which has a matching body
Failure/Error: set_up_provider_state interaction.provider_state, options[:consumer]
Pact::ProviderVerifier::SetUpProviderStateError:
Error setting up provider state 'When I want the values' for consumer 'Consumer' at http://localhost:9222/provider-states. response status=500 response body=
3) Verifying a pact between Consumer and Values API Given When I want the values Getting a list with GET /api/values returns a response which includes headers "Accept" with value "application/json"
Failure/Error: set_up_provider_state interaction.provider_state, options[:consumer]
Pact::ProviderVerifier::SetUpProviderStateError:
Error setting up provider state 'When I want the values' for consumer 'Consumer' at http://localhost:9222/provider-states. response status=500 response body=
4) Verifying a pact between Consumer and Values API Given When I want the values Getting a list with GET /api/values returns a response which includes headers "Content-Type" with value "application/json"
Failure/Error: set_up_provider_state interaction.provider_state, options[:consumer]
Pact::ProviderVerifier::SetUpProviderStateError:
Error setting up provider state 'When I want the values' for consumer 'Consumer' at http://localhost:9222/provider-states. response status=500 response body=
1 interaction, 1 failure
Failed interactions:
* Getting a list given When I want the values
I guess my question is, do I need to actually test the HTTP calls to /api/values or am I missing something else?
Thanks
So after speaking to a colleague and getting a hint, it was down to the Startup class. I hadn't realised it effectively starts the web application so
public class TestStartup
{
public void Configuration(IAppBuilder app)
{
var startup = new Startup();
app.Use<ProviderStateMiddleware>();
startup.Configuration(app);
}
}
calls the startup class within the application:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var httpConfig = new HttpConfiguration();
httpConfig.MapHttpAttributeRoutes();
httpConfig.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var appXmlType = httpConfig.Formatters.XmlFormatter.SupportedMediaTypes
.FirstOrDefault(t => t.MediaType == "application/xml");
httpConfig.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
app.UseWebApi(httpConfig);
}
}
and my unit test passed. Hope this helps someone.

ASP.NET Web API "400 Bad Request" on POST Request When Deploying to Test Server

I have a Single Page Application (SPA) with AngularJS as my front-end and .NET Web API as my backend. Everything works fine on my development machine, i.e. when I run it from Visual Studio (2015) under localhost. However, once published to our testing server, when sending POST requests to the Web API I get a "400 Bad Request" error. GET requests work fine. I am debugging it with Fiddler and when I look in the TextView tab it says "The underlying provider failed to Open". Here are some screenshots from Fiddler:
This is how the request and response look on my local machine:
This is the response headers on the the test server:
And the TextView on the test server:
The data being sent through the POST request is the same for both the localhost and the test server. Also, for both of them an authorization header is present. Other than the values for "Referer" and "Host", the only other difference I am noticing between them is that localhost is running on IIS/10.0 while the test server is on IIS/8.0.
The code for the AngularJS resource which calls the WebAPI is the following:
(function () {
"use strict";
angular
.module("mainApp")
.factory("incidentResource", ["$resource", "baseApiUrl", incidentResource]);
/// The factory function. The Angular $resource service and the appSettings
/// constant are injected as parameters.
function incidentResource($resource, baseApiUrl) {
return {
generateFile: $resource(baseApiUrl + "/api/Imports/PreviewOverlay", null,
{
'generate': { method: 'POST' }
})
};
}
})();
This is called from Angular code like so:
vm.generate = function () {
incidentResource.generateFile.generate(vm.divisionImports,
function (data) {
vm.successMessage.show = true;
},
function (response) {
vm.message = response.statusText + "\r\n";
if (response.data.exceptionMessage) {
vm.message += response.data.exceptionMessage;
}
});
}
And finally, my Web API controller looks like this:
[RoutePrefix("api/Imports")]
public class ImportController : BaseController
{
[Authorize]
[HttpPost]
[Route("PreviewOverlay")]
public IHttpActionResult GenerateFile(DivisionImport[] chargedIncidents)
{
try
{
// Get the name of the user currently logged in
string UserName = this.GetCurrentUserIdFromRequest();
List<DivisionImport> incidentsList = new List<DivisionImport>();
incidentsList.AddRange(chargedIncidents);
this.incidentFileBuilder.GenerateFile(FileType.Delimited, incidentsList);
return this.Ok();
}
catch (Exception ex)
{
return this.BadRequest(ex.Message);
}
}
}
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class BaseController : ApiController
{
}
What could be causing this error?
As #Dan Jul pointed out, the error was caused by a faulty database connection string. While deploying my code to our test server, we changed the connection configuration file to a different one (for the test server) and it contained a connection string with a syntax error.
Things are working now.

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" });
}
}
}

Web api return 404

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.

Categories