API - HttpResponseMessage: (407) proxy authentication required - c#

Edit 1: Other Controller
public class identityController : ApiController
{
[HttpGet]
public async Task<IHttpActionResult> getfullname(string firstName)
{
string name = firstName;
return Ok(name);
}
}
I have created a controller which uses an API from another solution.
Method that i use in the controller looks like below:
public class GetNameController : ApiController
{
[HttpGet]
public async Task<IHttpActionResult> CalculatePrice(string firstName)
{
string _apiUrl = String.Format("api/identity/getfullname?firstName={0}", firstName);
string _baseAddress = "http://testApp.azurewebsites.net/";
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_baseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync(_apiUrl);
if (response.IsSuccessStatusCode)
{
return Ok(response);
}
}
return NotFound();
}
The result of response.IsSuccessStatusCode is always false. When i check the response values i see this result:
{
StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Connection: close
Date: Thu, 21 Jul 2016 12:28:21 GMT
Server: Microsoft-HTTPAPI/2.0
Content-Length: 334
Content-Type: text/html; charset=us-ascii
}
}
What could i be missing?

string _apiUrl = String.Format("api/identity/{0}", firstName);
This is assuming that your url is correct, and your testapp is up and running. Even though when I hit it azure tells me your app is stopped. You will need to get your app started first, then change the string _apiUrl to the suggestion above.
http://testapp.azurewebsites.net/api/identity/getfullname?firstName=steve
Gives me this message
Error 403 - This web app is stopped.
The web app you have attempted to reach is currently stopped and does
not accept any requests. Please try to reload the page or visit it
again soon.
If you are the web app administrator, please find the common 403 error
scenarios and resolution here. For further troubleshooting tools and
recommendations, please visit Azure Portal.
So there are several things in your identity controller that are going on.
the functions name is getFullName. Since the word get is in the name of the function. Any httpget request will be routed to the function automagically. Thus making the [HttpGet] redundant. This only works if there is 1 and only 1 httpget request in your controller. If there are multiple you will need to fully qualify the url like you have done
Since youa re using the [httpget] method attribute I can assume you are using webapi2. That being the case and you are using a
primitive in your controller argument you can do notneed to fully
qualify the parameter name on your call. ?firstname={0} changes to
/{0}

Related

HttpClient 400 bad request on PostAsync to endpoint after switching from Minimal API to MVC API

I started out with a .NET 6 minimal api which was working fine, however I needed some extra functionality, so I then decided to switch over to MVC style api. Since then, when my WPF app tries to call the endpoint using POST I get a 400 bad request.
If I use swagger to make the call, it works with no problem. But using HttpClient in my WPF app gives the error. I've looked at about a dozen other questions on this error but none of the solutions seemed to fix my problem.
When I run both the API and WPF app with debugging and debug the beginning of both the UserService call to InsertUser and the API's InsertUser methods, the API's debug point never gets hit, which to me sounds like when MVC is processing the request it sees something it doesn't like and returns the 400 error, but I'm not sure why the PUT request is fine and the POST request isn't.
In my ASP.net core program.cs I added the following to enable MVC api usage
builder.Services.AddControllers();
app.MapControllers();
In my WPF app the httpClient is handled through DI and injected in my UserService class used to make the call to the endpoint.
InsertUser POST method
public async Task<bool> InsertUser(UserModel user)
{
var result = await _httpClient.PostAsync("/api/Users",
user.AsJsonContent());
return result.IsSuccessStatusCode;
}
This is the UpdateUser method that uses PUT and works fine
public async Task<bool> UpdateUser(UserModel user)
{
var result = await _httpClient.PutAsync("/api/Users",
user.AsJsonContent());
return result.IsSuccessStatusCode;
}
Extension that turns the UserModel into a StringContent
public static StringContent AsJsonContent(this object obj)
{
string json = JsonConvert.SerializeObject(obj);
var content = new StringContent(json, Encoding.UTF8, "application/json");
//content.Headers.Remove("Content-Type");
//content.Headers.Add("Content-Type", "application/json");
//content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return content;
}
This is a trimmed down version of my UsersController
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
[HttpPost]
public async Task<IActionResult> InsertUser(UserModel model)
{
_appDbContext.Users.Add(model);
var result = await _appDbContext.SaveChangesAsync();
return result > 0 ? Ok() : Problem("User was not inserted");
}
[HttpPut]
public async Task<IActionResult> UpdateUser(UserModel model)
{
if(await _appDbContext.Users.FindAsync(model.Id) is UserModel efModel)
{
_mapper.Map(model, efModel);
_appDbContext.Users.Update(efModel);
var result = await _appDbContext.SaveChangesAsync();
return result > 0 ? Ok() : Problem("User was not updated");
}
return Problem("User not found.");
}
}
This is result from PostAsync
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
{
Date: Thu, 07 Jul 2022 14:35:53 GMT
Server: Kestrel
Transfer-Encoding: chunked
Content-Type: application/problem+json; charset=utf-8
}}
This is my response message
{Method: POST, RequestUri: 'https://localhost:7159/api/Users', Version: 1.1, Content: System.Net.Http.StringContent, Headers:
{
Content-Type: application/json
Content-Length: 253
}}
Edit: Adding reqested info
Here is the json responce for the bad responce
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-ac9fc01b742547b2525eef9de28ea6a0-d123c5b603ff637c-00",
"errors": {
"Token": [
"The Token field is required."
]
}
}
This was the minimal api version of InsertUser
//Registered in another class
app.MapPost("/Users", InsertUser);
public async Task<bool> InsertUser(UserModel model)
{
await _appDbContext.Users.AddAsync(model);
var result = await _appDbContext.SaveChangesAsync();
return result > 0;
}
since you are posting the data as a json content, you have to add a frombody attribute to both controller actions
public async Task<IActionResult> UpdateUser([FromBody]UserModel model)

No candidates found for the request even though the route exists

I have a Controller which has the following declaration
[Authorize(Roles = Role.Admin)]
[ApiController]
[Route("meta/[controller]")]
public class ActionParameterController : BaseController<ActionParameterController>
Inside it there is the following method
[HttpPost("insert/{action}/{entity}")]
public IActionResult InsertActionParameter(
[FromBody] MetaActionParameter parameter,
int action,
int entity)
However when I try to do the POST request to this endpoint I get 404.
The url:
http://localhost:5000/meta/actionParameter/insert/2/2
Console output is:
2021-08-19 14:03:35.311 (Microsoft.AspNetCore.Hosting.Diagnostics.POST) [Information] Request starting HTTP/1.1 POST http://192.168.14.104:5000/meta/actionParameter/insert/1/2 - 0
2021-08-19 14:03:38.116 (Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware.) [Verbose] All hosts are allowed.
2021-08-19 14:03:38.117 (Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.POST) [Debug] "POST" requests are not supported
2021-08-19 14:03:38.120 (Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.POST) [Debug] "POST" requests are not supported
2021-08-19 14:03:38.125 (Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.POST) [Debug] "POST" requests are not supported
2021-08-19 14:03:38.132 (Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.) [Debug] AuthenticationScheme: "Bearer" was not authenticated.
2021-08-19 14:03:38.180 (Microsoft.AspNetCore.Routing.Matching.DfaMatcher.) [Debug] No candidates found for the request path '"/meta/actionParameter/insert/1/2"'
2021-08-19 14:03:38.182 (Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.) [Debug] Request did not match any endpoints
2021-08-19 14:03:38.185 (Microsoft.AspNetCore.Server.Kestrel.) [Debug] Connection id ""0HMB303OBKKOL"" completed keep alive response.
2021-08-19 14:03:38.191 (Microsoft.AspNetCore.Hosting.Diagnostics.POST) [Information] Request finished HTTP/1.1 POST http://192.168.14.104:5000/meta/actionParameter/insert/1/2 - 0 - 404 0 - 2880.056
I tried getting all routes from IActionDescriptorCollectionProvider and the route is printed. There are other routes in that controller that do work.
If I change the route to be only "insert" the request goes through. From what I can see, there is no conflict between routes.
What further steps should I take to diagnose this issue?
I think I know what the problem is.
I think the action and/or insert words are probably reserved words.
I tested with the following code and it worked:
[HttpGet("insert/{xaction}/{xentity}")]
public IActionResult InsertActionParameterGet(int xaction, int xentity)
{
Console.WriteLine(xaction + " " + xentity);
return new JsonResult("Ok");
}
Notice I just added an "x" to action and entity, just as a test.
Also, I did a test on a GET action.
Before adding the "x", it was not working, so I do think those are reserved words probably.
You need to write the complete router at Http Method Attribute, like this:
[HttpPost("meta/ActionParameter/insert/{action}/{entity}")]
public IActionResult InsertActionParameter([FromBody] MetaActionParameter parameter, int action, int entity)
There is a similar question here: C# Web Api 2 PUT and POST requests "not supported"
Based on that one, it looks like you should not be using [FromBody].
So your method signature should look something like this:
[HttpPost("insert/{action}/{entity}")]
public IActionResult InsertActionParameter(int action, int entity)
To ready the body of the request, you could do something like this:
var bodyStr = "";
var req = context.HttpContext.Request;
// Allows using several time the stream in ASP.Net Core
req.EnableRewind();
// Arguments: Stream, Encoding, detect encoding, buffer size
// AND, the most important: keep stream opened
using (StreamReader reader
= new StreamReader(req.Body, Encoding.UTF8, true, 1024, true))
{
bodyStr = reader.ReadToEnd();
}
// Rewind, so the core is not lost when it looks the body for the request
req.Body.Position = 0;
// Do whatever work with bodyStr here

404 error when posting to Web API 2 action using HttpClient

OK, there is lots going on here so I will try and keep my question and examples as simple as I can. With that in mind, please ask if you need any additional information or clarification on anything.
The code
I have a Web API 2 project which has a number of controllers and actions. The particular action I am having problems with is defined in the ContactController as follows:
[HttpPost]
public MyModel GetSomething(System.Nullable<System.Guid> uid)
{
return GetMyModel(uid);
}
In case it matters, my routing is setup as follows:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = RouteParameter.Optional }
);
Now I have another project that is required to call the above action. For calling the Web API I am using HttpClient. Note that I have lots of other actions calls which are working correctly, so this isn't a connectivity issue.
The code I am using to call the Web API method is as follows:
using (HttpClient client = GetClient())
{
var obj = new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("uid", someGuid.ToString()) };
var response = client.PostAsync(path, new FormUrlEncodedContent(obj)).Result;
return response.Content.ReadAsAsync<T>().Result;
}
In this instance, path is basically:
localhost:12345/api/contact/getsomething
The problem
The PostAsync call Result (i.e. response in the above code) gives this message:
{StatusCode: 404, ReasonPhrase: 'Not Found', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Pragma: no-cache
X-SourceFiles: =?UTF-8?B?QzpcRGV2ZWxvcG1lbnRcUHJvamVjdHNcTGltYVxMaW1hIHYzXERFVlxMaW1hRGF0YVNlcnZpY2VcYXBpXHVzZXJhY2Nlc3NcZ2V0bW9kdWxlc2FjY2Vzcw==?=
Cache-Control: no-cache
Date: Fri, 18 May 2018 10:25:49 GMT
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Content-Length: 222
Content-Type: application/json; charset=utf-8
Expires: -1
}}
If I put a breakpoint inside the aciton then it doesn't fire. However, what I find strange is that when I call it, Visual Studio (2018) tells me that the specific action has a "failed request" on that specific action. So clearly it must know which method I am trying to call?
At this point I am running out of ideas on how to debug further. What am I doing wrong here?
in this case you can use the same endpoint as for getting and posting.
so you probably need:
[HttpGet]
public IActionResult Get(System.Nullable<System.Guid> uid)
{
return GetMyModel(uid); //make sure you got it, oterhwise return a NotFound()
}
[HttpPost]
public IActionResult Post(InputModel model)
{
_service.doMagicStuff();
return Ok();
}
cheers!
not sure but error may be because of you are passing keyvalue pair
var obj = new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("uid", someGuid.ToString()) };
var response = client.PostAsync(path, new FormUrlEncodedContent(obj)).Result;
instead of guild only i.e. only string value expected by function , so it will be
var response = client.PostAsync(path, new FormUrlEncodedContent(someGuid.ToString())).Result;
method should be
[HttpPost]
public MyModel GetSomething(string uid)
{
return GetMyModel(Guid.Parse( uid));
}
You are sending the guid with the FormUrlEncodedContent but the requests content type is application/json.
I recommend you to send it as a json like this
using (HttpClient client = GetClient())
{
var obj = new { uid = someGuid.ToString()) };
var json = JsonConvert.SerializeObject(obj);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var result = client.PostAsync(path, content).Result;
return response.Content.ReadAsAsync<T>().Result;
}
Then in the api controller, use the FromBody attribute to declare that the parameter is read from the request body
[HttpPost]
public MyModel GetSomething([FromBody]RequestModel model)
{
return GetMyModel(model.uid);
}
public class RequestModel
{
public System.Nullable<System.Guid> uid { get; set; }
}
Also, if you only have one Post method in the contact controller the url localhost:12345/api/contact will be enough

POST to ASP.NET Web API from Fiddler

I have an ASP.NET Web API. I am trying to POST a string to an endpoint. My ASP.NET Web API endpoint looks like the following:
[HttpPost]
public async Task<IHttpActionResult> Test(string name)
{
int i = 0;
i = i + 1;
return Ok();
}
In Fiddler, I execute the following request from the composer:
POST http://localhost:8089/api/MyApiController/test
If I remove "string name" as the parameter, I can successfully execute my API endpoint. When string name is there, I get a 405 error. So, I added the following in the "Request Body" section in Fiddler:
John
Unfortunately, that still causes a 405 to be thrown. I can't tell if I'm setting up my Web API endpoint wrong if I'm setting up my request in fiddler incorrectly. My full request looks like this:
POST http://localhost:8089/api/MyApiController/test HTTP/1.1
User-Agent: Fiddler
Host: localhost:8089
Content-Length: 26
Content-Type: application/json; charset=utf-8
{ "name" : "John"}
The response looks like this:
HTTP/1.1 405 Method Not Allowed
Cache-Control: no-cache
Pragma: no-cache
Allow: GET
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-SourceFiles: =?UTF-8?B?QzpcRWNvZmljXFNvbGlkUVxKTExcamxsLW1hcmtldHNwaGVyZVxXZWJzaXRlXGFwaVxTZWFyY2hBcGlcaW5kZXg=?=
X-Powered-By: ASP.NET
Date: Fri, 12 Dec 2014 15:51:50 GMT
Content-Length: 73
{"Message":"The requested resource does not support http method 'POST'."}
I don't understand why POST is allowed when I do not have a parameter. Yet, when I add a parameter, POST does not work.
[Update]
I added the following in my C# code:
var content = await Request.Content.ReadAsStringAsync();
I figured the JSON would be in the content. However, content is just an empty string.
Under the "Composer" tab:
I suggest you should try with following url
http://localhost:8089/api/MyApi/test
I think that problem is with route.
You should have some route like following as default route just have controller/id. This route comes before default route.
config.Routes.MapHttpRoute(
name: "DefaultApi1",
routeTemplate: "api/{controller}/{action}",
defaults: new { action="test" }
);
Also you action should look like this.
[HttpPost]
public async Task<IHttpActionResult> Test([Frombody]string name)
{
int i = 0;
i = i + 1;
return Ok();
}
in the body part of the composer try
{ "name" : "somtext"}
Below, I modified the datatype of the parameter from string to object, and had success.
public async Task<IHttpActionResult> Post( [ FromBody ] **object** items )
{
Console.WriteLine( items );
return Ok();
}
In the body you need to add Json or XML syntax, not just then name for The Asp.Net Deserialiser to do its thing. Json would be:
{ "name" : "John" }
EDIT: Your url route also seems incorrect, if you are using the default routes. By default it will be picked up as: POST http://localhost:8089/api/MyApiController/ the "test" part is omitted as it recognises this as a POST method. Your action should also specify that the parameter you take in ("name") is expected from the body public async Task<IHttpActionResult> Test([FromBody]string name). Now your url will match if you do something like:
`POST http://localhost:8089/api/MyApiController/`
User-Agent: Fiddler
Host: localhost:8089
Content-Length: 26
{ "name" : "John" }

Sending base64 string to c# server

I made a c# web api at the the moment . It is working fine until today.
I've tried to convert an image to a base64string, and then send the base64string to the c# server through ajax. When I did the said steps, an error occur.
XMLHttpRequest cannot load http://10.0.10.105:50231/api/hello. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://10.0.10.201' is therefore not allowed access.
I dont know where the main problem resides but in my observation, the error only occur when passing a very long base64string to the server because when i try to send short test string the problem wont appear and all is working well.
Do you have any idea what is the better remedy to fix this ? Or any other way to perform my needed objective ? my ajax code is something like this.
$.ajax({
type: 'POST', //GET or POST or PUT or DELETE verb
url: 'http://10.0.10.105:50231/api/hello', // Location of the service
contentType: 'application/x-www-form-urlencoded', // content type sent to server
data: { action: "3",pers_guid:"wew",base64image:"this-is-the-base64-image"},
//dataType: 'jsonp', //Expected data format from server
//processdata: true, //True or False
success: function (data) {//On Successfull service call
$scope.pageView = 2;
console.log("THE Data description for match : " + data[0].description);
},
error: function (msg) {// When Service call fails
alert(msg);
}
});
and my c# server is similar to this (this is not the whole code).
public class theHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken)
.ContinueWith((task) =>
{
HttpResponseMessage response = task.Result;
response.Headers.Add("Access-Control-Allow-Origin", "*");
return response;
});
}
}
public class HelloController : ApiController
{
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
//.... more code here
}
This is the result when I tried to pass a very long string...
Request URL:http://10.0.10.105:50231/api/hello
Request Headers CAUTION: Provisional headers are shown.
Accept:*/*
Content-Type:application/x-www-form-urlencoded
Origin:http://10.0.10.201
Referer:http://10.0.10.201/kiosk/
User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36
Form Dataview sourceview URL encoded
action:3
pers_guid:wew
base64image:the_long_base64_string
but when i pass just a sample string this is the result.
Remote Address:10.0.10.105:50231
Request URL:http://10.0.10.105:50231/api/hello
Request Method:POST
Status Code:200 OK
Request Headersview source
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:49
Content-Type:application/x-www-form-urlencoded
Host:10.0.10.105:50231
Origin:http://10.0.10.201
Referer:http://10.0.10.201/kiosk/
User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36
Form Dataview sourceview URL encoded
action:3
pers_guid:wew
base64image:sample_string
Response Headersview source
Access-Control-Allow-Origin:*
Content-Length:103
Content-Type:application/json; charset=utf-8
Date:Wed, 04 Jun 2014 01:02:35 GMT
Server:Microsoft-HTTPAPI/2.0
This error shows up because your server listening on '10.0.10.105' does not specify the 'Access-Control-Allow-Origin' header in its HTTP response. This is a common issue with websites that 'talk' to each other, and you may read about it here (or just Google 'CORS').
As a solution, have the server listening on that IP return the following header from the POST response:
Access-Control-Allow-Origin: *
This has some security implications that you might want to read about (in general it's best to not use the "allow-all" star '*' and instead specify the requesting server explicitly).
Update: As explained in that paper under "Preflight requests", non-trivial POST requests require the server to also listen to OPTIONS requests and return these headers in the response:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Can you try having the image server listen to OPTIONS (just like you did for GET, POST, etc.) and return these headers? (The client making the 'POST' request will automatically precede it with an OPTIONS request to the same server, and if the OPTIONS response contains these headers, the subsequent POST will be called.)
Thank you so much for your answers. It gives me lot of ideas about access origin. It seems that my problem is on the configuration. I added this code and all is working fine.
config.MaxReceivedMessageSize = 5000000;
The program will automatically show an access-control-allow-origin when it exceeds the max limit size of the default configuration.
Thank you so much for the ideas.
So, CORS and the Access-Control-Allow-Origin header are awkward. I ran into this one a while back when I had an API that needed to be referenced from JavaScript code running on several different websites. I ended up writing an ActionFilterAttribute to handle it for my API controllers:
[AttributeUsage(AttributeTargets.Method)]
public class AllowReferrerAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
var ctx = (System.Web.HttpContextWrapper)actionContext.Request.Properties["MS_HttpContext"];
var referrer = ctx.Request.UrlReferrer;
if (referrer != null)
{
string refhost = referrer.Host;
string thishost = ctx.Request.Url.Host;
if (refhost != thishost)
ctx.Response.AddHeader("Access-Control-Allow-Origin", string.Format("{0}://{1}", referrer.Scheme, referrer.Authority));
}
base.OnActionExecuting(actionContext);
}
}
You can decorate your controller's methods with that attribute and it will add the correct Access-Control-Allow-Origin for your caller's website, regardless of what that website is. And assuming that you're using IIS as your host... won't work for OWIN-hosted sites.
Example usage:
public class HelloController : ApiController
{
[AllowReferrer]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}

Categories

Resources