Does NOT support http GET when sending POST - asp.net-mvc

tried looking around the web a bit and am at a loss.
currently setting up a REST api.
locally using postman i send the POST to the end point and all is well.
once its pushed to the test server and i run the POST
Status: 405 Method Not Allowed
{
"message": "The requested resource does not support http method 'GET'."
}
controller looks like and as said, I am able to post locally just not to test server
[HttpPost, Route("")]
[ResponseType(typeof(string))]
public async Task<IHttpActionResult> CreateSomething([FromBody] obj stuff)
c# generated by post man
var client = new RestClient("http://test-api.someurl.com/makestuff/makethisthing/");
var request = new RestRequest(Method.POST);
request.AddHeader("postman-token", "not sure if this is needed but post man put it here");
request.AddHeader("cache-control", "no-cache");
request.AddHeader("authorization", "Bearer [A Very long OAuth token]");
request.AddHeader("content-type", "application/json");
IRestResponse response = client.Execute(request);
it works fine locally running on local iis (not iis express) on my pc and other dev pc. once put to test server then get that error message. i know i am using post as i am using postman with {{server}} env var and just changing from local to test env so the post itself is not chaining at all
there are currently a handful of other end points in separate web applications that are working fine.
any point in the right direction, thank you

Since you're sure you're requesting via POST, but the error unambiguously says you're attempting a GET, the only thing that makes sense is that there's a redirect occurring. A redirect will actually return a 301 or 302 with a Location header. The client then requests that new URL there via GET, even if the original request was a POST. In other words, the flow would be something like: POST -> 302 -> GET -> 405.

Name the action method with a specific route. E.g. /controller/create-something-post/
[HttpPost, Route("create-something-post")]
[ResponseType(typeof(string))]
public async Task<IHttpActionResult> CreateSomething([FromBody] obj stuff)
{
}

Related

Chrome does not follow 302 Location redirect (to other origin) after HTTP POST

tl;dr; How do I get Chrome to follow the 302 'Location' redirect to a different domain after an HTTP POST?
I'm using OpenID Connect, using an external provider to provide authentication services to my ASP.NET MVC (C#, .NET 6) application.
I have a controller action to close an account. In this instance,
[HttpPost("close-account")]
public async Task<IActionResult> CloseAccount()
{
// This does not work as expected in a POST and the browser does not redirect
var properties = new AuthenticationProperties { RedirectUri = "..." }
return SignOut(properties, "Cookies", "OpenIdConnect");
}
There is a standard html <form> which is completing the post action to the controller via a <button>. No Javascript involved.
Looking at the Chrome developer tools, the browser receives the 302 Found with the Location response header correctly set to the URL the browser needs to redirect to in order to complete the sign out with the OpenID Connect provider.
However, the browser does not follow the redirect. I am presuming because it is cross-origin, and starts with "https://oidc.myauthenticationprovider.com/logout?...." which is a different domain.
I have verified this - because if I change the redirect to be another URL in the same site, then the browser follows up with a GET to the URL provided in the Location header. It's just that if the origin is a different domain, it does nothing.
I only see this behaviour with POST. I have a similar GET endpoint to sign out users (without closing their account- just a regular sign-out) - which works perfectly.
[HttpGet("sign-out")]
public async Task<IActionResult> CloseAccount()
{
// This works as expected in a GET and the user is redirected.
var properties = new AuthenticationProperties { RedirectUri = "..." }
return SignOut(properties, "Cookies", "OpenIdConnect");
}
I'm not sure this is strictly a CORS issue because it is the response to an HTTP POST rather than an XHR request, however, I have tried the below in case it is related to CORS:
I have tried the services.AddCors method in the application startup, adding the origin
I have tried manually adding the Access-Control-Allow-Origin header to both the original GET and also the POST response of the close account page
These do not make a difference.
To the user, the behaviour is as if the form has done nothing. They press the button, and it seems like nothing at all happens. The code within the controller executes (in my example, the account does get closed), but the redirection is then ignored.
This was due to us setting the form-action: self directive in CSP. As described on MDN Web Docs,
Warning: Whether form-action should block redirects after a form
submission is debated and browser implementations of this aspect are
inconsistent (e.g. Firefox 57 doesn't block the redirects whereas
Chrome 63 does).
For us this was what was causing the blocked redirect to a different domain after form submission.

Saturn API not responding to GET when using acceptJson

The F# Saturn web framework fails on retrieving a value for GET method when acceptJson is a part of pipeline.
Below a sample code that I run to reproduce the issue:
let api = pipeline {
plug acceptJson
set_header "x-pipeline-type" "Api"
}
let apiRouter = router {
not_found_handler (setStatusCode 404 >=> text "Api 404")
pipe_through api
get "/test" (text "Hello world")
}
let appRouter = router {
forward "/api" apiRouter
}
appRouter is then added in the use_router section of the application code.
When I'm sending the request with a header Content-Type:application/json the response is "404 not found". But if I remove plug acceptJson from the api pipeline definition I get a correct response.
How to make Saturn work with the plug acceptJson?
I suspect 3615 is right - this seems similar to a problem I just solved yesterday after beating my head against the wall for a week. A request to my app (just a straight app from "dotnew new Saturn") from my browser was accepted. But a request to the same url from a test method returned a 404. It boiled down to the fact that the test request was missing an "Accept" header. When I added "Accept text/html", it worked. What I deduced from that is that, if the app can't find a content type that will be accepted according to the request, then it will report a 404. Your situation is the same - you're trying to return Json, but the request didn't include an "Accept application/json", so it can't find any page that the request would accept.
Of course, I could be wrong.

BigCommerce oAuth auth token request always returning 401

I can not figure out what I'm doing wrong. I'm developing an App for BigCommerce and can not get the simple oAuth exchange to work correctly.
The initial get request is being made to https://www.my-app.com/oauth/bigcommerce/auth. This is the code in the controller for that request. It's a Laravel 5.6 app:
use Illuminate\Http\Request;
use Bigcommerce\Api\Client as Bigcommerce;
class BigcommerceOAuthController extends Controller
{
public function auth(Request $request)
{
$object = new \stdClass();
$object->client_id = 'my-client-id';
$object->client_secret = 'my-client-secret';
$object->redirect_uri = 'https://my-app.com/oauth/bigcommerce/auth';
$object->code = $request->get('code');
$object->context = $request->get('context');
$object->scope = $request->get('scope');
$authTokenResponse = Bigcommerce::getAuthToken($object);
$storeHash = str_replace('stores/', '', $request->get('context'));
Bigcommerce::configure(array(
'client_id' => 'my-client-id',
'auth_token' => $authTokenResponse->access_token,
'store_hash' => $storeHash
));
echo "<pre>";
print_r($authTokenResponse);
print_r(Bigcommerce::getTime());
echo "</pre>";
}
}
Every time I try to install my draft app from the BigCommerce control panel, I get an error because $authTokenResponse is not an object. When I debug further into the Bigcommerce\Api\Connection class, I can see that the response from the server is empty, and the status is a 401, which means "Unauthorized".
I can't figure out why I am getting this error. As far as I can see, I'm doing everything right. I've tried urlencoding the string retrieved from $request->get('scope'), since that string becomes unencoded by Laravel, but that didn't seem to help.
I am also confused how this is even supposed to work at all. In the BigCommerce docs, they show this example POST request, which uses application/x-www-form-urlencoded Content-Type and passes the request body as a url encoded string:
POST /oauth2/token HTTP/1.1 Host: login.bigcommerce.com Content-Type:
application/x-www-form-urlencoded Content-Length: 186
client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}&code=qr6h3thvbvag2ffq&scope=store_v2_orders&grant_type=authorization_code&redirect_uri=https://app.example.com/oauth&context=stores/{STORE_HASH}
However, if you inspect what's going on in the Connection class, you can see that the Content-Type is being set to application/x-www-form-urlencoded as the docs say, but the request body is being passed in as a json string, not a url string. Shouldn't the request be a url encoded string as the docs suggest?
A couple of things here to check:
Do you have a public URL where you can receive the Auth Callback?
If so, did the store owner registered the app successfully? https://developer.bigcommerce.com/api/registration
When you have the client_id and secret_id. You should have all of the details needed to send a POST request to the BC Auth Token Service at https://login.bigcommerce.com/oauth2/token
The content uses URL encode Make sure to URL encode your content. Be careful of of the encoding of & and = signs when those are actually being used as separators.
More details can be found in this post:
Can BigCommerce Private Apps use OAuth

502 Bad Gateway on POST/PUT Web API Calls from ASP.NET Core MVC

I'm getting strange errors in ASP.NET Core when calling Web API that I have created for the application. GET requests go through fine and return all of the data that they should, but my POST/PUT commands all return a 502, specifically from the MVC application. I can call the API's from Postman and get a proper response and the object is created in the database.
502 - Web server received an invalid response while acting as a
gateway or proxy server. There is a problem with the page you are
looking for, and it cannot be displayed. When the Web server (while
acting as a gateway or proxy) contacted the upstream content server,
it received an invalid response from the content server.
I am impersonating an Integrated Windows Login with the following code for all web requests to the API:
async Task Action()
{
response = await _service.CreateIncident(model);
}
await WindowsIdentity.RunImpersonated(identity.AccessToken, Action);
CreateIncident(model):
using (var client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true }))
{
var newIncident = new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json");
var response = await client.PostAsync(hostUri, newIncident);
return response;
}
There is also one GET Request that I make through Ajax to get an incremented ID to display to the user before they create their new Incident that returns a 502 Bad Gateway as well. Is this an IIS Setting that is incorrect?
If you use WindowsIdentity.RunImpersonated and an asynchronous function, it will not work. You must be synchronous when doing non-GET requests. I have updated my GitHub issue, I'm hoping to get this bug addressed. If you are a future visitor to this topic, you can see where this ended up here.
I think it also depends on the size of the data. Smaller packages work, larger ones don't.

POST request and Node.js without Nerve

Is there any way to accept POST type requests without using Nerve lib in Node.js?
By default the http.Server class of Node.js accepts any http method.
You can get the method using request.method (api link).
Example:
var sys = require('sys'),
http = require('http');
http.createServer(function (request, response) {
response.writeHead(200, {'Content-Type': 'text/plain'});
response.write(request.method);
response.end();
}).listen(8000);
sys.puts('Server running at http://127.0.0.1:8000/');
This will create a simple http server on the port 8000 that will echo the method used in the request.
If you want to get a POST you should just check the request.method for the string "POST".
Update regarding response.end:
Since version 0.1.90, the function to close the response is response.end instead of response.close. Besides the name change, end can also send data and close the response after this data is sent unlike close. (api example)

Resources