Reactjs to Webapi on ASP.net Core 2.2 fails with error 415 in app but not in Postman - reactjs

It can do a GET OK, but it just can't do a POST. I have tried all manner of Header combinations. Its to the localhost.
Here is my Reactjs App call
export function saveStaff(data) {
return fetch(baseUrl, {
mode: "no-cors",
method: "POST",
headers: {
Host: "localhost:44370",
Allow: "GET, POST",
Accept: "application/json, text/plain",
"Content-Type": "application/json"
},
body: { Name: "tuesday", Department: "tuesday", visitorcount: 0 } // data // JSON.stringify(data)
})
.then(handleResponse)
.catch(handleError);
}
Here are the headers from Postman this works!
POST /api/StaffNamesAPI HTTP/1.1
Host: localhost:44370
Allow: GET, POST
Content-Type: application/json
Accept: application/json, text/plain
User-Agent: PostmanRuntime/7.16.3
Cache-Control: no-cache
Postman-Token: a5b282c7-24e7-46a6-ba22-4f74a31fa9bd,2232ec6c-b3e9-4e29-88e3-abf63675486c
Host: localhost:44370
Accept-Encoding: gzip, deflate
Content-Length: 122
Connection: keep-alive
cache-control: no-cache
{"Name": "Wednesday TestWithPostman",
"Department": "Groan",
"visitorcount": 0 }
Here is the API Controller
[HttpPost]
[Consumes("application/json")] //https://github.com/aspnet/AspNetCore/issues/4396
public async Task<ActionResult<StaffNames>> PostStaffNames(StaffNames staffNames)
{
_context.StaffNames.Add(staffNames);
await _context.SaveChangesAsync();
return CreatedAtAction("GetStaffNames", new { id = staffNames.Id }, staffNames);
}
My class is simple at this stage
public class StaffNames
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public int VisitorCount { get; set; }
}
And in my startup.cs I have the CORS set up
//https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-2.2#ecors
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("http://localhost:3000/",
"http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
Here is my useCors
app.UseCors(MyAllowSpecificOrigins);
//https://stackoverflow.com/questions/52896068/reactasp-net-core-no-access-control-allow-origin-header-is-present-on-the-r
app.UseCors(builder => builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
Thanks for your help, I have been pondering this for hours!

If you have two projects, you need to set your mode as cors.And I met the same problem of CORS.Finally, I overcome it by removing the / in your original url like
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
builder =>
{
//Do not use `http://localhost:3000/`
builder.WithOrigins("http://localhost:3000",
"http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
Configure method:
app.UseCors(MyAllowSpecificOrigins);
React:
return fetch(baseUrl, {
mode: "cors",
method: "POST",
headers: {
Host: "localhost:44370",
Allow: "GET, POST",
Accept: "application/json, text/plain",
"Content-Type": "application/json"
},
body: JSON.stringify({ Name: "tuesday", Department: "tuesday", visitorcount: 0 })
})

For some reason using no-cors mode prevents setting Content-Type header value to application/json and it's set to test/plain by default which causes the issue. After some research I've found out that mode cors works fine for your case, so first of all consider learning more about all those modes and choose most suitable one for you.
And, secondly, you need to add [FromBody] attribute you action parameter in order to allow model binder parse json body on server side. On client side you need to serialize object to json before sending it since fetch doesn't do this for you. The minimal working code looks like this
Controller
[HttpPost]
[Consumes("application/json")]
public IActionResult PostStaffNames([FromBody]StaffNames staffNames)
React
export function saveStaff(data) {
return fetch(baseUrl, {
mode: "cors",
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(handleResponse)
.catch(handleError);
}

Related

Jeresy CORS filter working but React rest GET failing still with header ‘access-control-allow-origin’ is not allowed

I create a rest react front end to talk to a Jersey servlet on tomcat on the back end for RH 8.6. When react tried to do on REST GET or POST commands I got the "‘access-control-allow-origin’ is not allowed according to header" error. So I then added the CORS filter which was suppose to fix the origin problem, but the react client is still failing. I have tried different filters but there is no change. I assume the problem is in the react GET fetch but it looks ok with me and gets a header back when mode: 'no-cors' is set. In the debugger the CORSFilter class gets the GET, but it does not reach the resource class endpoint so its getting rejected.
Using postman I have verified the CORSFilter is inserting the values in the response as you can see here.
POST http://localhost:8080/rtc-servlet/mcd/location
Headers from postman tool:
Status Code: 200
access-control-allow-credentials: true
access-control-allow-headers: X-Requested-With, CSRF-Token, X-Requested-By, Authorization, Content-Type
access-control-allow-methods: API, GET, POST, PUT, DELETE, OPTIONS, HEAD
access-control-allow-origin: *
access-control-max-age: 151200
connection: keep-alive
content-length: 701
content-type: application/json
date: Sat, 10 Dec 2022 02:52:19 GMT
keep-alive: timeout=20
servlet code:
#Provider
public class CORSFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
// *(allow from all servers) OR https://crunchify.com/
responseContext.getHeaders().add("Access-Control-Allow-Origin", "*");
// As part of the response to a request, which HTTP headers can be used during the actual request.
responseContext.getHeaders().add("Access-Control-Allow-Headers",
"X-Requested-With, CSRF-Token, X-Requested-By, Authorization, Content-Type");
Also tried these options:
"Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
responseContext.getHeaders().add("Access-Control-Allow-Credentials", "true");
responseContext.getHeaders().add("Access-Control-Allow-Methods",
"API, GET, POST, PUT, DELETE, OPTIONS, HEAD");
// How long the results of a request can be cached in a result cache.
responseContext.getHeaders().add("Access-Control-Max-Age", "151200");
}
}
#GET // read in updated/original files
#Produces(MediaType.APPLICATION_JSON) // what format we send back
public JsonObject getLocationValues() {
System.out.println("Called location getLocationValues ");
return locationRepository.readConfigFile(false);
}
React Rest GET fetch:
const urll1 = "http://localhost:8080/rtc-servlet/mcd/location";
useEffect(() => {
const fetchPost = async () => {
await fetch(urll1, {
// mode: 'no-cors',
headers: {
'Content-Type': 'application/json',
"Accept": "application/json",
"Access-Control-Allow-Origin": "*",
},
})
.then((response) => {
if (response.ok) {
response.json().then(data => {
console.log("response fetchPost :" + JSON.stringify(data));
setPosts1(data);
});
} else {
console.log("response was not ok");
}
})
.catch((err) => {
console.log(err.message);
});
};
fetchPost();
}, []);
The console error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/rtc-servlet/mcd/location. (Reason: header ‘access-control-allow-origin’ is not allowed according to header ‘Access-Control-Allow-Headers’ from CORS preflight response).
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/rtc-servlet/mcd/location. (Reason: CORS request did not succeed). Status code: (null).
NetworkError when attempting to fetch resource.
So does anyone see that I am doing wrong?
After read the CORS not working posts in stackoverflow again I came across a commit about getting the origin from the header and then setting Access-Control-Allow-Origin to it vs. "*" and react on port localhost:3000 started to get responses back from the localhost:8080 servlet (origin is being set to "localhost:3000"). This was the forum string if you want to read up on it:
How to enable Cross domain requests on JAX-RS web services?.
So the change in the filter class is as follows:
String origin = requestContext.getHeaderString("origin");
if ((origin != null) && (!origin.isEmpty())) {
headers.add("Access-Control-Allow-Origin", origin);
} else {
// *(allow from all servers) OR https://crunchify.com/
headers.add("Access-Control-Allow-Origin", "*");
}
headers.add("Access-Control-Allow-Credentials", "true");
and in the js script "Access-Control-Allow-Origin": "*" was deleted:
await fetch(urll1, {
headers: {
'Content-Type': 'application/json',
"Accept": "application/json"
},
})
I am not sure if I now need the else since "*" didn't work for me, but I left it in. If its not needed or I am just doing something that sort of works because I am using firefox please let me know.

Access a remote api from frontend react.js without any server side code

I am trying to access remote url from fetch/axios post api in react.js. Here is my code in react-
const requestOptions = {
method: 'POST',
crossDomain: true,
mode: 'cors', // no-cors, *cors, same-origin
credentials: 'same-origin', // include, *same-origin, omit
headers:{'Content-Type': 'application/x-www-form-urlencoded',
"Access-Control-Allow-Origin": "*",
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Methods': 'GET,POST,OPTIONS,DELETE,PUT'},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer',
body:new URLSearchParams({
'store_id':'storeid',
'store_passwd':'storepass',
'total_amount':100,
'currency':'BDT',
'tran_id':'NF04',
'success_url':"https://tigrow.herokuapp.com",
'fail_url':"https://tigrow.herokuapp.com",
'cancel_url':"https://tigrow.herokuapp.com",
'cus_name':"nizam",
'cus_email':"test#test.com",
'cus_add1':"customer address",
'cus_add2':"customer address",
'cus_city':"Dhaka",
'cus_state':"Dhaka2",
'cus_postcode':"Dhaka",
'cus_country':"Bangladesh",
'cus_phone':"01700000000",
'cus_fax':"01700000000",
'ship_name':"Sha",
'ship_add1':"dhaka",
'ship_add2':"dhaka1",
'ship_city':"Dhaka1",
'ship_state':"Dhaka2",
'ship_postcode':"1000",
'ship_country':"Bangladesh",
'multi_card_name':"mastercard,visacard,amexcard",
'value_a':"ref001_A",
'value_b':"ref002_B",
'value_c':"ref003_C",
'value_d':"ref004_D",
'shipping_method':"No",
'product_name':"Test",
'product_category':"Test Category",
'product_profile':"general"
})
};
fetch(url, requestOptions)
.then(response =>console.log(response))
.then(data => console.log(data));
I want to get data from rempte api in react only, not any server side code. Here my content-type is application/x-www-form-urlencoded. How can I solve this problem only using react.js?
My remote API strictly mentioned that no call from client side code. Developer must need to call the API from server side and after completing the call developer should return the string url, not any json data. I followed the way and done the code in python and got the result. However my frontend was react. Here is the code snippet-
def sslcommerz_payment_gateway(request):
gateway_auth_details = PaymentGatewaySettings.objects.all().first()
settings = {'store_id':gateway_auth_details.store_id,
'store_pass': gateway_auth_details.store_pass,
'issandbox': True} #gateway_auth_details.store_pass, 'issandbox': False}
print(request.POST)
sslcommez = SSLCOMMERZ(settings)
post_body = {}
post_body['total_amount'] =request.POST.get('total_amount')
post_body['currency'] = request.POST.get('currency')
post_body['tran_id'] =unique_transaction_id_generator()
post_body['success_url'] = 'http://127.0.0.1:8000/api/payment/success/'
post_body['fail_url'] = 'http://127.0.0.1:8000/api/payment/faild/'
post_body['cancel_url'] = request.POST.get('cancel_url')
post_body['emi_option'] = 0
post_body['cus_name'] = request.POST.get('cus_name')
post_body['cus_email'] =request.POST.get("cus_email")
post_body['cus_phone'] = request.POST.get("cus_phone")
post_body['cus_add1'] = request.POST.get("cus_add1")
post_body['cus_city'] = request.POST.get("cus_city")
post_body['cus_state'] =request.POST.get("cus_state")
post_body['cus_postcode'] =request.POST.get("cus_postcode")
post_body['cus_country'] = request.POST.get("cus_country")
post_body['shipping_method'] ="NO"#request.POST.get("shipping_method")
post_body['multi_card_name'] = "mastercard,visacard,amexcard,mobilebank,internetbank,othercard"
post_body['num_of_item'] = request.POST.get("num_of_item")
post_body['product_name'] = request.POST.get("product_name")
post_body['product_category'] = request.POST.get("product_category")
post_body['product_profile'] = "Art(Hard Copy/Soft Copy)"
response = sslcommez.createSession(post_body)
print(response)
return 'https://sandbox.sslcommerz.com/gwprocess/v4/gw.php?Q=pay&SESSIONKEY=' + response["sessionkey"]
Finally I got the API response and returned a url.

My fetch doesn't upload the JSON string, I can't see the error in my code

I'm using Slim v4 for my REST API for testing purposes.
I want to fetch a JSON Data string to my REST API for saving some events.
public async callSaveEvent(event: EventList) {
let url: string = config.basePath + "eventList/saveEventList";
console.table(JSON.stringify(event));
await fetch(url, {
method: 'POST',
mode: 'no-cors',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ event })
}).then(response => {
if (!response.ok) {
throw new Error("Something is bad");
}
}).catch(error => {
console.error("Das ist passiert!: ", error);
});
}
This is my current Code. If I use the fetch.options.mode == "cors", I recieve in Slim that this Method is not allowed. Method is OPTIONS instead of POST. Because of this I using mode == "no-cors".
$param = $req->getParsedBody();
$param_ = $param;
$resp->getBody()->write($param);
return $resp;
}
This is my Backend Code. When I try to read the parsedBody, its just empty.
If I send a request with PostMan its accept the data and I get the data in the $param variable.
Can someone find some errors? I can't find them.

ASP.NET WebAPI2 CORS: null request in GetOwinContext on preflight

I'm creating an AngularJS (Typescript) SPA with a WebAPI2 backend, requiring authentication and authorization from the API. The API is hosted on a different server, so I'm using CORS, mainly following the guidance found at http://www.codeproject.com/Articles/742532/Using-Web-API-Individual-User-Account-plus-CORS-En as I'm a newcomer in this field.
All works fine, I can register and login, and then make requests to restricted-access controller actions (here the dummy "values" controller from the default VS WebAPI 2 template) by passing the received access token, in a client-side service with this relevant code:
private buildHeaders() {
if (this.settings.token) {
return { "Authorization": "Bearer " + this.settings.token };
}
return undefined;
}
public getValues(): ng.IPromise<string[]> {
var deferred = this.$q.defer();
this.$http({
url: this.config.rootUrl + "api/values",
method: "GET",
headers: this.buildHeaders(),
}).success((data: string[]) => {
deferred.resolve(data);
}).error((data: any, status: any) => {
deferred.reject(status.toString() + " " +
data.Message + ": " +
data.ExceptionMessage);
});
return deferred.promise;
}
Now, I'd like to retrieve the user's roles once logged in so that the AngularJS app can behave accordingly. Thus I added this method in my account API (which at the class level has attributes [Authorize], [RoutePrefix("api/Account")], [EnableCors(origins: "*", headers: "*", methods: "*")] (* are for testing purposes):
[Route("UserRoles")]
public string[] GetUserRoles()
{
return UserManager.GetRoles(User.Identity.GetUserId()).ToArray();
}
I then added this code to my login controller:
private loadUserRoles() {
this.accountService.getUserRoles()
.then((data: string[]) => {
// store roles in an app-settings service
this.settings.roles = data;
}, (reason) => {
this.settings.roles = [];
});
}
public login() {
if ((!this.$scope.name) || (!this.$scope.password)) return;
this.accountService.loginUser(this.$scope.name,
this.$scope.password)
.then((data: ILoginResponseModel) => {
this.settings.token = data.access_token;
// LOAD ROLES HERE
this.loadUserRoles();
}, (reason) => {
this.settings.token = null;
this.settings.roles = [];
});
}
where the account controller's method is:
public getUserRoles() : ng.IPromise<string[]> {
var deferred = this.$q.defer();
this.$http({
url: this.config.rootUrl + "api/account/userroles",
method: "GET",
headers: this.buildHeaders()
}).success((data: string[]) => {
deferred.resolve(data);
}).error((data: any, status: any) => {
deferred.reject(status.toString() + ": " +
data.error + ": " +
data.error_description);
});
return deferred.promise;
}
Anyway this triggers an OPTIONS preflight request, which in turn causes a 500 error. If I inspect the response, I can see that the GetOwinContext method gets a null request. Here is the beginning of the error stack trace:
{"message":"An error has occurred.","exceptionMessage":"Value cannot be null.\r\nParameter name: request","exceptionType":"System.ArgumentNullException","stackTrace":" at System.Net.Http.OwinHttpRequestMessageExtensions.GetOwinContext(HttpRequestMessage request)\r\n at Accounts.Web.Controllers.AccountController.get_UserManager() ...}
Yet, the code I'm using for GETting the roles is no different from that I use for GETting the dummy "values" from the WebAPI test controller. I can't exactly see the reason why a preflight should be required here, but in any case I'm getting this nasty exception in OWIN code.
My request header is (the API being at port 49592):
OPTIONS /api/account/userroles HTTP/1.1
Host: localhost:49592
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://localhost:64036
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36
Access-Control-Request-Headers: accept, authorization
Accept: */*
Referer: http://localhost:64036/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,it;q=-5.4
Could anyone explain?
I think I found some sort of working solution even if it looks somewhat dirty, but at least it works. I'm posting it here so other can eventually take advantage of it, but I'm open to suggestions. (Sorry for the bad formatting, but I tried several times and the editor does not allow me to correctly mark the code).
Essentially, the solution was suggested by the answer to this post: Handling CORS Preflight requests to ASP.NET MVC actions, but I changed the code which did not work for me (WebAPI 2 and .NET 4.5.1). Here is it:
in Global.asax, method Application_Start, add BeginRequest += Application_BeginRequest;.
add the override, which simply responds to OPTIONS requests by allowing everything (this is OK in my testing environment):
protected void Application_BeginRequest(object sender, EventArgs e)
{
if ((Request.Headers.AllKeys.Contains("Origin")) &&
(Request.HttpMethod == "OPTIONS"))
{
Response.StatusCode = 200;
Response.Headers.Add("Access-Control-Allow-Origin", "*");
Response.Headers.Add("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");
string sRequestedHeaders = String.Join(", ",
Request.Headers.GetValues("Access-Control-Request-Headers") ?? new string[0]);
if (!String.IsNullOrEmpty(sRequestedHeaders))
Response.Headers.Add("Access-Control-Allow-Headers", sRequestedHeaders);
Response.End();
}
}
the attribute decorating the accounts controller method is just the RouteAttribute:
[Route("UserRoles")]
public string[] GetUserRoles()
{
string id = User.Identity.GetUserId();
Debug.Assert(id != null);
string[] aRoles = UserManager.GetRoles(id).ToArray();
return aRoles;
}
This way the OPTIONS request gets a proper response and the successive GET succeeds.
ADDITION
I must also add that the EnableCors attribute is not enough as we must not only handle the OPTIONS verb, but also ensure that any CORS request gets the Access-Control-Allow-Origin header. Otherwise, you might observe an apparently correct response (code 200 etc) but see the $http call failing. In my case I add to global.asax this line:
GlobalConfiguration.Configuration.MessageHandlers.Add(new CorsAllowOriginHandler());
My CorsAllowOriginHandler is a DelegatingHandler which simply ensures that this header with value * is present in each response where the request included a Origin header:
public sealed class CorsAllowOriginHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync
(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
// all CORS-related headers must contain the Access-Control-Allow-Origin header,
// or the request will fail. The value may echo the Origin request header,
// or just be `*`.
if ((request.Headers.Any(h => h.Key == "Origin")) &&
(response.Headers.All(h => h.Key != "Access-Control-Allow-Origin")))
{
response.Headers.Add("Access-Control-Allow-Origin", "*");
}
return response;
}
}

in Web API 2 how to accept simple types as post parameter along with Route parameter

Hi after some struggle I am finally got past the angular js hurdle to pass the proper parameters to my server, but the web api 2 service fails to accept it.
below is the sample code
[RoutePrefix("api/v2/bids")]
public class BidsController : ApiController
{
[Route("{quoteId:long}/accept")]
public HttpResponseMessage AcceptQuote(long quoteId,[FromBody] string remarks)
{
HttpResponseMessage response;
response = Request.CreateResponse(HttpStatusCode.Accepted, quoteId);
return response;
}
}
if you notice i have both the route parameter and also a post parameter of type sting. When I post using fiddler with the following:
POST http://127.0.0.1:81/api/v2/Bids/101/accept? HTTP/1.1
Authorization: Basic a2lyYW5AYWJjc2hpcHBlci5jb206a2lyYW5AYWJjc2hpcHBlci5jb20=
Accept: application/json, text/plain, */*
Content-Type: application/json;charset=utf-8
Referer: http://127.0.0.1:81/shipper/
Accept-Language: en-US
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0; EIE10;ENUSWOL)
Host: 127.0.0.1:81
Content-Length: 40
DNT: 1
Connection: Keep-Alive
Pragma: no-cache
{"remarks":"Accepting this as the best"}
or use angularjs function:
function acceptQuote(quoteId, accept_remarks, fnSuccess, fnError) {
return $resource("/api/v2/Bids/:id/accept", { quoteId: "#id"},
{ "AcceptQuote": { method: "POST", isArray: false } })
.AcceptQuote({ id: quoteId }, { remarks: accept_remarks }, fnSuccess, fnError);
}
returns the following error:
{"Message":"The request is invalid.","ModelState":{"remarks":["Error reading string. Unexpected token: StartObject. Path '', line 1, position 1."]}}
i expected that using [FromBody] was sufficient to pass the simple types as post parameters, any ideas to what else I am missing here.
The [FromBody] is working a bit differently. Please, check this Parameter Binding in ASP.NET Web API. If you'd like to get the string [FromBody] string remarks, then your body must look like:
"Accepting this as the best"
Not a JSON. On the other hand, if the body contains the JSON, the most natural way how to consume that with ASP.NET Web API, is via the Entity/Object. So, we can create this
public class MyObject
{
public string remarks { get; set; }
}
And the Controller action should look like this:
[Route("{quoteId:long}/accept")]
public HttpResponseMessage AcceptQuote(long quoteId, MyObject myObject)
{
var remarks = myObject.remarks;

Resources