In interface of IApplicationService I have decorated one of my method as [HttpOptions]
When I do ajax from another domain, the method will be fired from the server side but I have got the below exception in Client-Side:
Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
As This link described how to enable CORS in MVC core or WebApi in ASPNetBoilerplate but I have neither app.UseCors("AllowAll") nor services.AddMvc(); in ASP.Net MVC with ABP.
I am using https://aspnetboilerplate.com/.
Is there a way to enable it in ABP ?
Sample:
abp.services.fep.user.ping().done(function (data) {
console.log(data);
});
but the result:
General
Request URL:http://xxxx/wado1/api/services/fep/user/Ping
Request Method:OPTIONS
Status Code:200 OK
Remote Address:192.168.16.201:80
Referrer Policy:no-referrer-when-downgrade
Response Headers:
Access-Control-Allow-Origin:*
Cache-Control:private
Content-Length:129
Content-Type:application/json; charset=utf-8
Date:Mon, 12 Feb 2018 10:06:41 GMT
Server:Microsoft-IIS/10.0
Set-Cookie:Abp.Localization.CultureName=en-US; expires=Tue, 12-Feb-2019
10:06:33 GMT; path=/WADO1
Set-Cookie:.ASPXANONYMOUS=jE6RBHsyANH3AuPkKFlnusuKGyqfdikCJ5cJSo38xg-
pCqe8JnUOX3t0o2V6s0_aT3N0PLnw2XG9PRzCPIse3uLC60vS8l-
3H8Z_UP1lqCIyDunM9oxhxqKJOca1B4Nv5eQwaQ2; expires=Sun, 22-Apr-2018 20:46:33
GMT; path=/; HttpOnly
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET
X-StackifyID:V1|80000cf1-0001-f700-b63f-84710c7967bb|
Request Headers
Accept:*/*
Accept-Encoding:gzip, deflate
Accept-Language:en-US,en;q=0.9
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:POST
Cache-Control:no-cache
Connection:keep-alive
Host:xxxx
Origin:http://localhost
Pragma:no-cache
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/64.0.3282.140 Safari/537.36
When I create a new template from https://aspnetboilerplate.com with ASP.Net Core frame work I can see something like below in Startup file:
// Configure CORS for angular2 UI
services.AddCors(
options => options.AddPolicy(
_defaultCorsPolicyName,
builder => builder
.WithOrigins(
// App:CorsOrigins in appsettings.json can contain more than one address separated by comma.
_appConfiguration["App:CorsOrigins"]
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.RemovePostFix("/"))
.ToArray()
)
.AllowAnyHeader()
.AllowAnyMethod()
)
);
UPDATE1:
I add the below webapi config to enable CORS:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.EnableCors();
}
}
and register it :
public override void Initialize()
{
WebApiConfig.Register(GlobalConfiguration.Configuration);
}
but in client-side I got Error 500, after enabling Failed Request Tracing, I see the log trace and buffer error like:
{"message":"An error has occurred.","exceptionMessage":"There is an action Ping defined for api controller fep/user but with a different HTTP Verb. Request verb is POST. It should be Options","exceptionType":"Abp.AbpException","stackTrace":" at Abp.WebApi.Controllers.Dynamic.Selectors.AbpApiControllerActionSelector.GetActionDescriptorByActionName(HttpControllerContext controllerContext, DynamicApiControllerInfo controllerInfo, String actionName)\r\n at Abp.WebApi.Controllers.Dynamic.Selectors.AbpApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext)\r\n at System.Web.Http.Cors.AttributeBasedPolicyProviderFactory.SelectAction(HttpRequestMessage request, IHttpRouteData routeData, HttpConfiguration config)\r\n at System.Web.Http.Cors.AttributeBasedPolicyProviderFactory.GetCorsPolicyProvider(HttpRequestMessage request)\r\n at System.Web.Http.Cors.CorsMessageHandler.d__10.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Cors.CorsMessageHandler.d__b.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Cors.CorsMessageHandler.d__0.MoveNext()"}
UPDATE2:
I enabled CORS by :
var cors = new EnableCorsAttribute(
origins: "*",
headers: "*",
methods: "*");
config.EnableCors(cors);
and register it in application_start but by this ajax with contentType:application/x-www-form-urlencoded" as #AlperEbicoglu posted in his answer method get fired in Back-End but entered DTO be null because it is JSON...
Abp set contentType to application/json automatically in abp.jquery.js file, for this reason(application/json) when I want run below code:
abp.services.fep.user.authenticate(box_in).done(function (data) {
console.log(data);
});
I got that mentioned exception in UPDATE1 section.
Why it can be called with application/x-www-form-urlencoded but with application/json exception raised (I see this in XML Tacer file in IIS).
I think 1 step remained two resolve this, any help would be truly appreciated.
Thanks in advance.
updated my answer....
You already succeeded in enabling CORS. Now you have a problem with preflight requests. I think you need to decorate your method as POST verb. Browser automatically sends preflight request if it needs.
and if you insist on making it OPTIONS. so you need to execute it with custom ajax like you can use JQuery ajax.
something like this:
$.ajax({
url: .....,
type: 'OPTIONS',
contentType: "application/x-www-form-urlencoded",
data: .......,
success: function (data, status) {
console.log("Success!!");
console.log(data);
console.log(status);
},
error: function (xhr, desc, err) {
console.log(xhr);
console.log("Desc: " + desc + "\nErr:" + err);
}
});
pls read this: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Last time i had a similar requirement with abp i have addressed it with custom attribute instead of enablecors method like below:
public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext filterContext)
{
if (filterContext.Response != null)
if (filterContext.Response.Headers != null)
filterContext.Response.Headers.Add("Access-Control-Allow-Origin", "*");
base.OnActionExecuted(filterContext);
}
}
and then in WebApiModule:
Configuration.Modules.AbpWebApi().DynamicApiControllerBuilder
.ForAll<IApplicationService>(typeof(WebsiteApplicationModule).Assembly, "api")
.WithConventionalVerbs()
.WithFilters(new AllowCrossSiteJsonAttribute())
.Build();
if you still getting error regarding Access-Control-Allow-Headers try to add it also in response headers
based on this thread: Setting Access-Control-Allow-Origin in ASP.Net MVC - simplest possible method
Related
Can someone tell me why am i getting these errors.
GET http://127.0.0.1:9000/api-docs/service.json
200 OK 4ms swagger-ui.js (line 30261)
Unable to Load SwaggerUI /api-docs/ (line 83)
Cross-Origin Request Blocked: The Same Origin Policy disallows
reading the remote resource at http://127.0.0.1:9000/api-
docs/service.json. This can be fixed by moving the resource to the
same domain or enabling CORS.
uncaught exception: Can't read from server. It may not have the
appropriate access-control-origin settings.
I am trying to run Swagger UI on port say 9090 and the Swagger API documentation at 9000 and trying to display the documentation in the UI.
I have added the CORS filter on API Documentation server (port 9000) as follows.
FilterHolder cors = swaggerUIContext.addFilter(CrossOriginFilter.class,"/*",EnumSet.of(DispatcherTyp‌ e.REQUEST));
cors.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
cors.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "");
cors.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD");
cors.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "Content-Type, api_key, Authorization");
The Request and Response headers in firefox V33.0 are
Response Headers
Content-Length 428
Content-Type application/json
Request Headers
Accept application/json;charset=utf-8,*/*
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Connection keep-alive
Host localhost:9000
Origin http://localhost:9090
Referer http://localhost:9090/api-docs/
User-Agent Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:33.0)
Gecko/20100101 Firefox/33.0
Here is how I am setting the CORS on server
final ResourceHandler swaggerUIResourceHandler = new ResourceHandler();
swaggerUIResourceHandler.setResourceBase("target/classes/api-docs");
final ServletContextHandler swaggerUIContext = new ServletContextHandler();
swaggerUIContext.setContextPath("/api-docs");
swaggerUIContext.setHandler(swaggerUIResourceHandler);
FilterHolder cors = swaggerUIContext.addFilter(CrossOriginFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
cors.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
cors.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
cors.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD");
cors.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "Content-Type, api_key, Authorization");
ServletHolder def = new ServletHolder("default", DefaultServlet.class);
def.setInitParameter("resourceBase","./http/");
def.setInitParameter("dirAllowed","false");
swaggerUIContext.addServlet(def,"/");
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { swaggerUIContext, new DefaultHandler() });
server.setHandler(handlers);
Did you do something funky with the json file?
I faced the same error, while trying to modify my JSON file and seeing the changes on Chrome. Ensure that the json itself is not breaking somehow, an extra bracket, comma and so on. I started from scratch with one of the example json from the swagger live demo and I was fine. I know it's a task but worked for me, atleast the UI loaded!
You can also go through the swagger ui readme, CORS support section
If you are using Spring Security
please add this code.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().configurationSource(request ->
{
CorsConfiguration cors = new CorsConfiguration();
cors.setAllowedMethods(
Arrays.asList(HttpMethod.DELETE.name(),HttpMethod.GET.name(), HttpMethod.POST.name()));
cors.applyPermitDefaultValues();
return cors;
}).httpBasic();
}
Explanation:
In the above CorsConfiguration class I'm using two methods.
cors.applyPermitDefaultValues();
cors.setAllowedMethods(List of Request Type name);
This method cors.applyPermitDefaultValues(); will allow cross origin request for all hosts.
Usually this method support cross origin support for these 3 request type methods GET,HEAD and PUT.
If your API exposing PUT , DELETE or any other request methods. Then you need to override it by this cors.setAllowedMethods();
I was able to get it working by adding the following method to my Application. Note this also opens up the API so that you can accept CrossOrigin requests. The details of the addMapping() bit are up to you, but this example opens up everything from everywhere. Here is the full class.
#SpringBootApplication
#EnableSwagger2
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*");
}
};
}
}
I also had this issue and after inspecting the headers in the pet-store example, I found that "Access-Control-Allow-Headers" needs "Content-Type, api_key, Authorization".
Make sure that you have api_key as well as I had that missing.
I have just encounter what way be a similar problem: Swagger UI: HTTP Content-type "application/json" causes "Unable to Load SwaggerUI".
Try changing the HTTP Content-type header of your GET service.json response from "application/json" to "text/html", or even removing it. I don't know why, but it seems it makes a difference for Swagger UI.
For Springdoc OpenAPI the following fix the issue:
#OpenAPIDefinition(servers = {#Server(url = "/", description = "Default Server URL")})
I was receiving this error while making a call to '/oauth/token' when I was making an HTTP call to the server instance running on my own system. I fixed this by creating a filter like this:
#Component
public class SimpleCORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
HttpServletRequest httpServletRequest = (HttpServletRequest) req;
if (httpServletRequest.getMethod() != "OPTIONS") {
chain.doFilter(req, res);
} else {
// In case of HTTP OPTIONS method, just return the response
}
}
I have added it as a filter in WebConfigurer:
private void initCORSFilter(ServletContext servletContext, EnumSet<DispatcherType> disps) {
log.debug("Registering CORS Filter");
FilterRegistration.Dynamic corsFilter = servletContext.addFilter("corsFilter", new SimpleCORSFilter());
Map<String, String> parameters = new HashMap<>();
corsFilter.setInitParameters(parameters);
corsFilter.addMappingForUrlPatterns(disps, true, "/*");
corsFilter.setAsyncSupported(true);
}
I am getting this error in FireFox:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://182.176.221.94:9091/ams/oauth/token. (Reason: CORS preflight channel did not succeed).
In short I was making sure the the preflight OPTIONS call always gets a response to proceed ahead. This was working on my own system, but now that the server instance is deployed on a Linux server, I am again getting this issue. And I am getting this only on calling 'oauth/token', everything other call is working fine.
What can I possibly do to get rid of this. Any help?
Your filter does not need to be annotated with #Component and it should be mapped with adequate url pattern in WebConfigurer class like other filters used in JHipster.
Also your filter should not break the filter chain as it does for OPTIONS. It's not consistent to allow OPTIONS method in header and then to not return the headers if you are processing an OPTIONS request.
The problem was that I was using != comparison for if (httpServletRequest.getMethod() != "OPTIONS". I changed it to if (!httpServletRequest.getMethod().equalsIgnoreCase("OPTIONS")) instead and it worked. This could have something to do with the fact that I tested on local machine while running the codebase but created a WAR file out of it and deployed on the server where it didn't work. I am not sure what exactly is the reason, but this fixed the issue.
I've been going crazy here trying to get jquery.ajax to work with ie9. So I have a ASP Web API 2 Rest API that implements CORS. CORS requests from all browsers work. IE9 didnt work since it uses the XDomainRequest. I managed to get it too work by making a custom implementation of ajaxTransport for IE9.
Right now GET requests seem to work fine. But when I do a post request from IE9 I get a HTTP error 415 - unsuportted media type.
I've set the content-type to:"application/json" and I've also tried "application/x-www-form-urlencoded", but from what I understood XDomainRequest doesnt support everything with custom headers? Does anybody know if something specific needs to be setup on the WebAPI or do I need to tweak the request?
My request looks like this:
$.ajax({
url: hostname + "/api/DDC/Book",
type: "POST",
contentType: "application/json",
data: {
DealID: function () {
return viewModel.get("DealID");
},
LocationID: function () {
return viewModel.get("LocationID");
},
Time: function () {
return viewModel.get("selectedDateTime.Time");
}
}
})
On the server I have this:
[HttpPost("DDC/Book")]
[EnableCors(origins: "*", headers: "*", methods: "POST, GET, OPTIONS, PUT, DELETE")]
public dynamic Post(BookModel model)
{
.........
When I analyze the failed request in the IE debugger this are the request headers that get sent out:
Key Value
Request POST //api/DDC/Book HTTP/1.1
Accept */*
Origin http://myurl.com
Accept-Language hr-HR
Accept-Encoding gzip, deflate
User-Agent Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Host www.somehost.com
Content-Length 55
DNT 1
Connection Keep-Alive
Cache-Control no-cache
I'm really losing all hope here and IE is making my go crazy (damn you Microsoft :D ), so any help or advice is much appriciated.
EDIT: From more reasearch I found out that WebAPI requires a content-type to work and XDomainRequest doesnt send out one. So the only solution I see is too tweak my webapi to have a default content-type when nothing is set. Don't know how to this yet though
EDIT2: Hacked my way through temporarily by transforming all my POSTs, to GETs, dont know how smart is this, but I see no bigger problem with it now, so it will do until I fix the problem
Managed to solve it myself. As pointed by Ray Nicholus when there is no Content-Type ASP Web API defaults to an "application/octet-stream" Content-Type. I need a default of "application/x-www-form-urlencoded".
I managed to achive this by writing my own simple message handler that checks an incoming requests "Content-Type" and if nothing is present it adds an "application/x-www-form-urlencoded" one.
This is the code:
public class DefaultContentTypeMessageHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Content.Headers.ContentType == null)
request.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
var response = await base.SendAsync(request, cancellationToken);
return response;
}
}
UPDATE:
As written by Robert Christ in the comment below I am extending the answer a bit for those who have not worked with message handlers before:
For those who don't understand at first glance, DelegatingHandlers
allow you to modify requests / response objects before they really hit
the WebAPI framework internals. Nothing else in the framework really
lets you modify the incoming request before model binding, without
actually writing custom model binders (eugh). so instead, here, you
can sniff out a null content type (which is guaranteed by shortcomings
in the XDomainRequest spec), update it to xml or json, and you will be
able to parse the incoming request correctly.
After you have written a message handler you need to register it with WebAPI. You do that in the WebApiConfig class:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MessageHandlers.Add(new DefaultContentTypeMessageHandler());
// Rest of your code
}
}
Just to confirm what you have already edited into your question over several updates: yes, XDomainRequest does not include a Content-Type header in the request. As you may know by now, you can't set any headers via this transport.
The lack of a Content-Type is particularly problematic for most server-side frameworks, as this means they will be unable to parse the content of the response automatically. In the absence of a Content-Type header, RFC 2616 says the body is assumed to be application/octet-stream, which is likely not what you want in this case. So, you'll need to "manually" parse the request body server-side by hard-coding the expected Content-Type for the associated request in this case.
I would strongly recommend you not simply convert all of your POSTs to GETs. GET requests should be "safe", per RFC 2616. By simply renaming all of your POSTs to GETs, you are no longer following the defined and accepted semantics of GET requests. In other words, don't do this.
Dennis' answer above uses async which is only available in .NET 4.5. For .NET 4 and possibly lower, use the following delegating handler instead:
public class DefaultContentTypeMessageHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Method == HttpMethod.Post && request.Content.Headers.ContentType == null)
{
request.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
}
return base.SendAsync(request, cancellationToken);
}
}
Also, don't forget your USING statements, you will need:
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
I have an action that is called using requests with application/jsonin the Content-type header. These requests will automatically create a JsonValueProvider that tries to deserialize the request's content. When the json is malformed, the value provider will throw an exception leading to the application's error page.
To reproduce this behavior, simply POST invalid json data to an action sending application/json as the Content-type header. This will trigger the exception.
[Edit]
Not much code is needed. Simply create an empty controller method and use a tool like Firefox "Poster" to send an invalid request to the action.
public class HomeController
{
public ActionResult Index()
{
return this.Json(true);
}
}
Then use Poster:
Set Content-type to application/json
Set Request content to {"This is invalid JSON:,}
Send the request
The result will be the full-blown standard ASP.NET HTML error page (either generic or custom, depending on your application).
[/Edit]
Since my action is called by embedded devices, I would like to send short responses, instead of the HTML error page. I would like to be able to create a response with status code 500, Content-type: text/plain, and the exception's message as it's content.
I have already tried a custom model binder and a custom error handler attribute but neither are called since the exception occurs earlier on in the processing pipeline. Is there a way to handle this error?
As a workaround, I have currently disabled the JsonValueProvider for the whole application and load the values from the request body myself. If there is a way to disable the JsonValueProvider on a per action basis, this would also help.
Thanks in advance for any pointers!
You could subscribe to the Application_Error event in your Global.asax and handle the exception as you want:
protected void Application_Error(object sender, EventArgs e)
{
var exception = Server.GetLastError();
Response.TrySkipIisCustomErrors = true;
Response.Clear();
Server.ClearError();
Response.StatusCode = 500;
Response.ContentType = "text/plain";
Response.Write("An error occured while processing your request. Details: " + exception.Message);
}
Seemed to me to be a beaten theme, but i couldn't find the answer. =(
I make jquery ajax requst to
localhost:666 from localhost:555 application
$.ajax({
url: "http://localhost:666/request",
dataType: 'json',
timeout: 5000,
success:...
i've got in chrome:
XMLHttpRequest cannot load http://localhost:666/request. Origin http://localhost:555 is not allowed by Access-Control-Allow-Origin.
What is the solution of the problem?
You can initiate cross-domain request in your webpage by creating either XMLHttpRequest object or XDomainRequest object. End user's web-browser will request data from the domain's server by sending an "Origin" header with the value of origin. If server responds with an "Access-Control-Allow-Origin: * | Origin" then we are permitted to access data; otherwise response will be unauthorized request.
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
// HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "http://AllowedDomain.com");
}
An article here: Cross-Origin requests and ASP.NET MVC
ajax calls are confined to parent domain only. for this a site on localhost:666 can not open ajax connection to localhost:555 since they belongs to different domain (or origin)
you need to try jsonp: http://www.google.com/search?q=jsonp
Try using dataType: 'jsonp', or $.getJSON function.