Cannot load web api method using Backbone.js - c#

I have a blank asp.net solution with 5 different projects. One of them is asp.net web api and other one is blank web site with bunch of html pages. I am using backbone.js/jQuery to make calls to the web api within the solution. My blank web site is running on different port and my web api is running on different port.
//Collection
var AuditsCollection = Backbone.Collection.extend({
url: 'http://localhost:56501/api/searchaudits',
sync: function (method, model, options) {
if (!options.crossDomain) {
options.crossDomain = true;
}
options.timeout = 1000;
alert(method + ": " + JSON.stringify(model));
return Backbone.sync(method, model, options);
},
});
var audits = new AuditsCollection();
// Model
var Audit = Backbone.Model.extend({ });
var audit = new Audit({ auditNumber: "A12" });
audits.add(audit);
// POST CALL - I am sending this model and expecting back another JSON object in response.
audit.save({}, {
success: function (response) {
alert("Got audits successfully" + response);
},
error: function (response) {
alert("Error.. Go home now");
}
});
I still get this error
XMLHttpRequest cannot load http://mydomain:56501/api/searchaudits. Request header field Content-Type is not allowed by Access-Control-Allow-Headers.

I think I figured out what the issue is. The project is setup in the following structure.
Error:
-- Solution
-- Project 1 (Web API) - running on `http://localhost:80/api`
-- Project 2
-- Project 3
-- Project 4 (Views) - running on `http://localhost:3000/`
So when I started making ajax requests, it started giving exceptions like
XMLHttpRequest cannot load http://localhost:80/api/searchaudits. Request header field Content-Type is not allowed by Access-Control-Allow-Headers.
OPTIONS localhost:80/api/searchaudits 405 (Method Not Allowed)
XMLHttpRequest cannot load /api/searchaudits
I did not know that it is going to consider domains with different ports ad different domains.
Solution:
Override Backbone.Sync method - reference
In your Web Api projects web.config file, add the following
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept"/>
</customHeaders>
</httpProtocol>
</system.webServer>
There are answers in StackOverflow regarding this issue in bits and pieces, but this is an effort to put the complete solution together in one place.

Related

HTTP PATCH no 'Access-Control-Allow-Origin' header in ASP.NET Core 6

I have reviewed the information on Enable Cross-Origin Requests .NET 6 to no avail.
This only affects the HttpPatch. When I run it in Swagger, no problems. When I try to test through 3rd party tool or CORS Test tool, I get errors.
The policy used dedicated for the CORS Test webapp already mentioned. Here is the code:
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://cors-test.codehappy.dev")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
When I run a test, I get this error message each time: It does not have the access-control-allow-origin header set to *. Without this header, requests from other domains cannot be made to it via a users browser.
All my other routes are OK, as I have enabled them on the Caddy Server globally using the following JSON snippet:
header {
# enable HSTS
Strict-Transport-Security max-age=31536000;
# disable clients from sniffing the media type
X-Content-Type-Options nosniff
# clickjacking protection
X-Frame-Options DENY
# keep referrer data off of HTTP connections
Referrer-Policy no-referrer-when-downgrade
# Content-Security-Policy: default-src 'self'
Access-Control-Allow-Origin *
Access-Control-Allow-Credentials true
Access-Control-Allow-Methods *
Access-Control-Allow-Headers *
}
On the particular route in the Controller that uses PATCH (there is only 1 controller using PATCH for 1 item), I have this code too (abbreviated to show the annotations):
[EnableCors("MyAllowAllHeadersPolicy")]
[HttpPatch("{id:int}")]
public async Task<IActionResult> PatchAsync(int id, ...
The long error message in the Console from the browser is:
Access to XMLHttpRequest at 'https://web.site' from origin 'http://localhost:4200'
has been blocked by CORS policy: Response to preflight request doesn't pass access
control check: It does hot have HTTP ok status.
I don't want to post the entire URI, as this exposes my API and there are no Authorisations setup whilst it is undergoing development mode, so the URL's are just for example purposes.
Ideally I want to handle the preflight request on the server side, in the WebApi project, so it returns a HTTP status code of 200 so that the browser will continue with sending the actual request.
I have read through quite a few documents and tried different policies, setting it to allow all, but to no avail.
Is there someone that might be able to point me in the right direction to resolve this?
My alternative, was to take a copy of the object, and re-publish it back to the API as a PUT command over the top of the object. This worked before but was a big overhead to handle the request. :-(
To enable the HTTP PATCH I referenced this Microsoft article: aspnet Core 6 JsonPatch
I created a .net 6 MVC app, and in my controller, I followed the document you mentioned and added a request like this:
[HttpPatch]
public IActionResult JsonPatchWithModelState([FromBody] JsonPatchDocument<Customer> patchDoc)
{
if (patchDoc != null)
{
var customer = new Customer {
customerName = "a"
};
patchDoc.ApplyTo(customer, ModelState);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return new ObjectResult(customer);
}
else
{
return BadRequest(ModelState);
}
}
Then in the Program.cs, adding cors policy:
builder.Services.AddControllersWithViews().AddNewtonsoftJson();
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyAllowAllHeadersPolicy",
policy =>
{
//policy.WithOrigins("https://cors-test.codehappy.dev")
policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
...
..
.
app.UseRouting();
app.UseCors("MyAllowAllHeadersPolicy");
And now test in the tool you mentioned :
But when I create a SPA to test the api
$("#btn2").click(function() {
alert(2);
$.ajax({
headers : {
'Accept' : 'application/json',
'Content-Type' : 'application/json'
},
url : 'https://localhost:7151/home/JsonPatchWithModelState',
type : 'PATCH',
data : JSON.stringify({customerName: "xxxx"}),
success : function(data) {
alert("OK");
},
error : function(data) {
alert("error");
}
});
});
It can access the api.

413 error in .NET Core 3.1 MVC web application [duplicate]

I'm running into a 413 issue while trying to send data from my web application (.netfx 4.6.1) to my web api (.net core 3.1). In the code below, I send a list over containing byte data of images along with additional data needed to build a file. The expected output is to return a byte array containing the new file. Unfortunately when sending the request I receive an error: Response status code does not indicate success: 413 (Request Entity Too Large).
The error only seems to occur when the file is large to begin with, which makes sense. The research I've done seems to point to settings in IIS, the main ones being maxAllowedContentLength, maxRequestLength, and uploadReadAheadSize. I've tried increasing these values to ones more suited to this process but nothing seems to work. I've adjusted them for both the web application and the web api, as I was not sure which one was causing the problem.
Where does the problem lie? In the application, the API, or both? Is there an additional setting I'm missing to allow an increased size? Is there an issue with how I'm sending the request? Any help is appreciated.
public static async Task<byte[]> CreatePdfFromImageFilesAsync(List<ImageFile> imageFiles)
{
var list = new List<dynamic>();
foreach (var item in imageFiles)
{
list.Add(new
{
Data = Convert.ToBase64String(item.Bytes),
PageOrder = item.PageOrder,
Rotation = item.Rotation,
Type = "PDF"
});
}
var response = _client.PostAsJsonAsync($"{FileCreatorAPI}/api/files/CreateFileFromMultiple", list).Result;
var result = response.EnsureSuccessStatusCode();
var bytes = await result.Content.ReadAsAsync<byte[]>();
return bytes;
}
Below changes worked for me
// If using Kestrel:
.Configure<KestrelServerOptions>(options =>
{
options.AllowSynchronousIO = true;
//options.Limits.MaxRequestBodySize = null; --did not worked
options.Limits.MaxRequestBodySize = int.MaxValue;
})
// If using IIS:
.Configure<IISServerOptions>(options =>
{
options.AllowSynchronousIO = true;
//options.MaxRequestBodySize = null;
options.MaxRequestBodySize = int.MaxValue;
});
create web.config file and add following configuration
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="2147483648" />
</requestFiltering>
</security>
</system.webServer>
</configuration>
Can you check that attribute https://github.com/aspnet/Announcements/issues/267 ?
Using
[RequestSizeLimit(100_000_000)]
on your controller entry point, or more globally setting it this way:
.UseKestrel(options =>
{
options.Limits.MaxRequestBodySize = null;
EDIT: article from MS: https://dotnet.microsoft.com/download/dotnet-core
I think the problem is on the server. The server is terminating the request because it exceeds it's configured maximum allowable request size.
Which server are you using?
For Nginx users, the directive which determines what the allowable HTTP request size can be is client_max_body_size, the default maximum allowable request size is 1MB.
The limit in Apache is set via the LimitRequestBody directive and defaults to 0 (meaning unlimited) to 2147483647 (2GB).
Check out this article on how you can fix it if you are using any of those two servers.

How to handle long time request in Angular 4?

I am having thouble with a request, in Angular 4, that takes really long.
The backend is in .Net Core 2.0 that connects to an Oracle DB. It should wait for a long query to run in the database to get the data and send it to the client. However, it seems that the client is unable to wait until the end of the process (the return of all records), what I get is the error:
Failed to load resource: the server responded with a status of 502
(Bad Gateway)
This error occurs when a CGI application does not return a valid set
of HTTP headers, or when a proxy or gateway was unable to send the
request to a parent gateway. You may need to get a network trace or
contact the proxy server administrator, if it is not a CGI problem.
It is not a Timeout error, as I thought it'd be.
Here is the code of my request:
exportExcel(dadosConsulta: DadosConsultaModel) : Promise<any>{
let url ="/api/Relatorios/exportToExcel";
return this.http
.post(url, JSON.stringify(dadosConsulta),{ headers: this.headers })
.timeout(500000)
.toPromise()
.then(data => data.json())
.catch(this.handleError);
}
How can I prevent this from happening?
I worked with oracle, long time request querys, and observables and didn't have that problem.
Something like this
login(account: string, password: string, user: string): Observable <any> {
let body = new URLSearchParams();
body.set('account', account);
body.set('password', password);
body.set('user', user);
return this.http.post(this.globalVars.apiUrl + 'login', body)
.map((res: any) => {
let result = res.json().data.result;
let data = res.json().data;
this.client.auth = {
authCode: result.authCode,
token: result.token,
};
this.firstLogin = data.firstLogin;
return this.client;
})
.catch((error: any) => {
console.log('error', error._body);
return Observable.throw(error._body);
})
.timeoutWith(25000, Observable.throw(new Error('Connection Error')))
}}
The problem was actually coming from IIS, and to solve this I had to add a web.config to my project (which is not created by default) and modify the 'aspNetCore' tag like this:
<aspNetCore **requestTimeout="00:20:00"** processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
Got it from this post: Timeouts with long running ASP.NET MVC Core Controller HTTPPost Method

Credentials flag is 'true', but the 'Access-Control-Allow-Credentials

I am trying to connect to a ASP.NET Web-API Web Service from an AngularJS page and I am getting the following
Credentials flag is 'true', but the 'Access-Control-Allow-Credentials' header is ''. It must be 'true' to allow credentials. Origin 'http://localhost:221' is therefore not allowed access.
var cors = new EnableCorsAttribute("http://localhost:221", "*","GET,PUT,POST,DELETE");
config.EnableCors(cors);
Using this AngularJS
$http({
method: 'GET',
url: 'http://localhost:1980/api/investors/62632',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
withCredentials: true
// withCredentials: true,
}).then(function onUserComplete(response) {
// this callback will be called asynchronously
// when the response is available
}, function onError(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
After reading many articles I add this to the web.config
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="http://localhost:221" />
</customHeaders>
</httpProtocol>
and I get this error message
The 'Access-Control-Allow-Origin' header contains multiple values localhost:221, localhost:221, but only one is allowed. Origin localhost:221 is therefore not allowed access.
Which really doesn't make any sense as I have added it once and it doesn't find it but I add it to web.config and get an error saying its been added multiple times. I have read many articles and can't seem to find the right answer. I am using Google Chrome. Would be very grateful for help as I am pulling my hair out right now.
For whom, who uses WebApiConfig.cs:
config.EnableCors(new EnableCorsAttribute("*", "*", "*") { SupportsCredentials = true });
The header is added twice once by the code and the other by the web.config. The CORS support is used to allow for the addition of headers for CORS purposes. The configuration custom headers also add response headers to any request, so you may want to remove the config setting.
var cors = new EnableCorsAttribute..
<customHeaders>
<add name="Access-Control-Allow-Origin" value="http://localhost:221" />
</customHeaders>
Since both of those areas are adding the same origin twice, you get the multiple values on the header.
When making an AJAX call with the parameter withCredentials: true, the response header should have the Access-Control-Allow-Credentials = true. You need to add that via code using SupportsCredentials = true for the CORS attributes. Otherwise you will get the error
"Credentials flag is 'true', but the 'Access-Control-Allow-Credentials is ''"
For more information, on the withCredential parameter and the response header look at this article:
http://www.ozkary.com/2015/12/api-oauth-token-access-control-allow-credentials.html
hope it helps.
I came across this question while trying to hit a webapi on .net core from an angular2 app. I had to add AllowCredentials() to the cors configuration in my Configure method in the Startup.cs to look like the following.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.UseCors(builder =>
builder
.AllowCredentials()
.WithOrigins("http://localhost:3000"));
...
}
Try the method outlined here for preflight requests:
enabling cross-origin resource sharing on IIS7
And use the Chrome extension Postman or Fiddler for easier debugging of CORS. I'm willing to bet that you are adding the header twice, but without your entire code, it is difficult to debug. (heck, CORS is difficult to debug even with the code).
To me, it appears that you shouldn't have both the web.config setting as well as the global EnableCors() attribute - this causes the doubles.
You don't appear to be adding the Access-Control-Allow-Credentials anywhere server side, but it might be added by the AllowCors attribute, I am not sure. (I am partial to handling CORS in OWIN myself)

OPTIONS preflight not handled in jQuery

I have a webpage at localhost:63342 with a jQuery ajax call in that webpage, to my webservice server at localhost:55000. In the webservice I set the Access-Control headers.
In Chrome's developer tools, Network tab, I can see that the OPTIONS preflight thing is sent, and the response header has the following, which looks great.
Access-Control-Allow-Headers:x-requested-with, X-Auth-Token, Content-Type, Accept, Authorization
Access-Control-Allow-Methods:POST, OPTIONS, GET
Access-Control-Allow-Origin:*
Cache-Control:private
Content-Length:0
Date:Fri, 06 Jun 2014 13:30:58 GMT
Server:Microsoft-IIS/8.0
However, the response to the OPTIONS request hits the error function of my jQuery ajax call. Developer tools shows me that the browser prepares the POST, but fails it because it thinks the resource does not have the Access-Control-Allow-Origin header set. The browser does not try to send the POST. Here is the error message from the console of the browser:
XMLHttpRequest cannot load http://localhost:55000/webservice/ws.svc/CreateOuting. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:63342' is therefore not allowed access.
Its as though jQuery is interfering in the OPTIONS, POST process. Any ideas on what I should do to make this work?
Here is my ajax call
$.ajax({
type: 'POST',
data: JSON.stringify(obj),
headers: { "Content-type": "application/json" },
url: base_url + 'CreateOuting',
crossDomain: true,
success: function (an_outing) {
$('#listviewOutings').listview('refresh', true);
$('#boxOutingName')[0].value = '';
myLib.OpenBasicPopup('Success', 'The outing name was saved.')
},
error: function (err) {
alert(err.statusText); // response to OPTIONS request ends up here
}
});
Here is how I set the headers in the method on the server (.NET C#):
public bh_Outing CreateOuting(string strOuting) {
try
{
//for all cors requests
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
//identify preflight request and add extra headers
if (WebOperationContext.Current.IncomingRequest.Method == "OPTIONS")
{
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Methods", "POST, OPTIONS, GET");
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Headers", "x-requested-with, X-Auth-Token, Content-Type, Accept, Authorization");
return null;
}
// do stuff
Here is the interface for that method. I dont think its perfect yet, but I dont think its the problem either.
[WebInvoke(UriTemplate = "*", Method = "*", ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
bh_Outing CreateOuting(string strOuting);
Thanks for taking a look at this. I am truly stuck.
Update, 6/17/14, 5:38 PM EST
I added a element to my webconfig as in this post, and this made no change to my results.
This may be not the reason, but have you tried enabling cors in jQuery? Before any cors ajax request:
jQuery.support.cors = true;
The cause of the problem that made me post the above question, seems to be that Chrome was incorrectly representing what was really happening. As I wrote above, what was happening was:
... the response to the OPTIONS request hits the error function of my
jQuery ajax call. Developer tools shows me that the browser prepares
the POST, but fails it because it thinks the resource does not have
the Access-Control-Allow-Origin header set. The browser does not try
to send the POST.
To try to get more detail, I installed and used Microsoft Message Analyzer. It showed that the OPTIONS request was being sent; the OPTIONS response was being sent; the POST request was being sent back to the webservice (!!!) and a POST response was being sent.
This was a huge breakthrough because now, instead of trying to solve a CORS problem (the error message that Chrome developer tools showed) I started working to solve a "Not Found" and then a "Bad Request" error (the error messages that MS Message Analyzer showed).
The other technique I used that helped a lot was that I set up tracing in my webservice. Tracing wrote stuff to a log file which default-opens in an event viewer and provided the details that I needed to figure out the (real) issues. Here is what I put in the webconfig to enable tracing.
<system.diagnostics>
<trace autoflush="true" />
<sources>
<source name="System.ServiceModel"
switchValue="Critical, Error, Warning"
propagateActivity="true">
<listeners>
<add name="myListener"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData= "c:\logs\ServiceModelExceptions.svcLog" />
</listeners>
</source>
</sources>
</system.diagnostics>
I made no changes to the CORS settings (jQuery and web.config) and its running fine in Chrome now. Also, I have the webservice out on the www, so it is truly cross-domain, not just localhost:63342 to localhost:55000.

Categories