ASP.Net MVC Async File Upload to API with Kendo fails - ajax

So this Kendo UI Upload worked well when I send files to MVC controller, but I have to change it and send the file directly to API controller, but it fails.
This is the Kendo Upload init code:
$("#files").kendoUpload({
async: {
saveUrl: "#WebApiHelper.GetUrl("Notification/UploadFile")",
removeUrl: "remove",
autoUpload: true
},
success: function(),
error: function()
}
On Chrome console it shows Load Canceled on the Status text of the request in Network tab:
The result of the request is like shown below:
Request URL:http://localhost:45706/api/Notification/UploadFile
Request Method:OPTIONS
Status Code:200 Ok
Request Headers
Accept:*/*
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:pt-BR,pt;q=0.8,en-US;q=0.6,en;q=0.4
Access-Control-Request-Headers:origin, content-type
Access-Control-Request-Method:POST
Host:localhost:45706
Origin:http://localhost:1654
Proxy-Connection:keep-alive
Referer:http://localhost:1654/
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31
Response Headers
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:origin, accept, content-type
Access-Control-Allow-Methods:GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin:*
allow-access-from domain:*
Cache-Control:no-cache
Content-Length:0
Date:Fri, 03 May 2013 16:53:30 GMT
Expires:-1
Pragma:no-cache
Server:Microsoft-IIS/8.0
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET
X-SourceFiles:=?UTF-8?B?QzpcVXNlcnNcR0F0ZWNcRG9jdW1lbnRzXEdBdGVjXE9kaXNzZWlhXEFwcGxpY2F0aW9uXEdBdGVjLkFncm9XZWIuQ29yZVxHQXRlYy5BZ3JvV2ViLkNvcmUuQVBJXGFwaVxOb3RpZmljYXRpb25cVXBsb2FkRmlsZQ==?=
Note that the status code is 200 because I returned it in the method. So it reaches the API method...
[HttpPost][HttpOptions]
public HttpResponseMessage UploadFile(HttpPostedFileBase files)
...but the files parameter is aways null.
This is the CORS related attributes on API webconfig file:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="allow-access-from domain" value="*" />
<add name="Access-Control-Allow-Headers" value="origin, accept, content-type" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
<add name="Access-Control-Allow-Credentials" value="true" />
</customHeaders>
</httpProtocol>
Any thoughts ? Any help will be very appreciated!

This code worked for me.
public ActionResult Document_Create([DataSourceRequest] DataSourceRequest request, Customer obj, IEnumerable<HttpPostedFileBase> Docs)
{
foreach (var Doc in Docs)
{
string newFileName = Path.GetFileName(Doc.FileName).Replace(" ",""); //Guid.NewGuid().ToString() + Path.GetExtension(Doc.FileName);
var physicalPath = Path.Combine(Server.MapPath("~/Documents/"), newFileName);
Doc.SaveAs(physicalPath);
}
return Json(new[] { obj }.ToDataSourceResult(request, ModelState));
}

Related

Error facing on communication between asp.net webapi server and angular2 application

I am facing the CORS related issue when i try to connect my angular2 application and asp.net webapi application.
Error:-
register:1 Access to XMLHttpRequest at 'http://localhost:49457/api/UserDetails' from origin 'http://localhost:4200' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:4200, http://localhost:4200', but only one is allowed.
Here is my code for connecting for calling asp.net webapi url through my angular2 app:-
User.service.ts
GetUser(userobj:user):Observable<User>
{
return this.http.get<User>(`http://localhost:49457/api/UserDetails`,{responseType:"text"})
.subscribe(
function(response)
{
console.log("user details retreived successfully");
},
function(error)
{
console.log(error);
});
}
This is my code for asp.net webapi,
public class UserDetailsController : ApiController
{
private sampledbEntities dbentity = new sampledbEntities();
// GET api/<controller>
public IQueryable<Userdetail> GetUserdetails()
{
return dbentity.userdetails;
}
}
Actually when i run my asp.netwebapi server it is retrieving data correctly through browser.And also i have enabled
CORS in webapiconfig.cs,
void Register(HttpConfiguration config)
{
var corsAttr = new EnableCorsAttribute("http://localhost:4200", "*", "*");
config.EnableCors(corsAttr);
}
in web.config.cs,
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="http://localhost:4200" />
<add name="Access-Control-Allow-Headers" value="Origin, Content-Type, X-Auth-Token" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
</customHeaders>
</httpProtocol>
After enabling CORS in Webapiconfig.cs and web.config also i am facing the same error.
Please clarify how do i come out of this error,
Replace http://localhost:4200 with "*" from EnableCorsAttribute method.
void Register(HttpConfiguration config)
{
var corsAttr = new EnableCorsAttribute("http://localhost:4200", "*", "*");
config.EnableCors(corsAttr);
}
and remove httpProtocol configuration settings from web.config.

Multiple CORS working for GET but not for PUT/POST with pre-flight requests Web api 2

When I configure CORS in my Web.config like following, every thing works:
<customHeaders>
<add name="Access-Control-Allow-Origin" value="http://localhost:3000"/>
<add name="Access-Control-Allow-Methods" value="GET, PUT, POST, DELETE, HEAD, OPTIONS"/>
<add name="Access-Control-Allow-Headers" value="accept, cache-control, credentials, content-type, authorization, origin, X-Requested-With, X-dev-mode"/>
</customHeaders>
However, when I try to enable multiple CORS like this:
// Filename: Global.asax.cs
protected override void Application_Start()
{
ApiConfig.Register(GlobalConfiguration.Configuration);
}
// Filename: ApiConfig.cs
public static void Register(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("http://localhost:3000,http://someOtherUrl.com/", "*", "*");
config.EnableCors(cors);
}
It works for GET (i.e. getting content, etc) but, when using POST/PUT (i.e. saving content, etc) it gives this error in browser (Chrome) console
Fetch API cannot load http://localhost:56214/api/1/content/SAVE. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
The request is failing on OPTIONS. Please note that I do have custom handlers and my routes are also configured.
One more thing to mention is that the PUT/POST call to save content is an ajax call via react. Can that be an issue?
The problem is the trailing slash in your second URL. Change it to this and it works:
var cors = new EnableCorsAttribute("http://localhost:3000,http://someOtherUrl.com", "*", "*");
have you tried removing the blank space to the Access-Control-Allow-Methods like
<add name="Access-Control-Allow-Methods" value="GET,POST,PUT,DELETE,OPTIONS" />

Web API 2 CORS IIS Express Debug and No Access-Control-Allow-Origin header

Empty web.api project, install Microsoft.aspnet.webapi.cors 5.2.3, add
config.EnableCors();
to webapiconfig. make controller and action
public class HomeController : ApiController
{
[EnableCors("*" , "*" , "*")]
public async Task<string> Get()
{
return await Task.FromResult("omg");
}
}
Debug app and Load up fiddler and do a request to http://localhost:61939/api/Home
there are no CORS headers present. web.config contains:
<system.webServer>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
what am I missing? Why would this not insert an Access-Control-Allow-Origin header in all request to my Get method?
Also the answer of defining CORS in web.config isn't an answer ... At some point I will need to add origin checking and potentially even checking the HTTP Method something like:
if(requestContext.HttpMethod == "POST" && (origin == "https://someplace.com" || origin == "http://localhost"))
What you've done is enough to enable CORS, you can also enable CORS on all the controllers using this code :
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
I'm not sure how you're testing it, but note that only once the request contains the Origin header, it returns the Access-Control-Allow-Origin header in reponse. If you omit the origin header in the request, the response wouldn't contain the Access-Control-Allow-Origin.

unable to make a remote POST to Web API (sending string) - not sure what happens

I have a web api method:
[HttpPost]
public IActionResult Post([FromBody]string entryInJson)
{ .... }
This method is hit by a remote domain, so CORS is needed. As i wasn't successful in finding the relevant cs file in my mvc6 app, i used this solution in web.config:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
When I do the post, using online tools like http://requestmaker.com/, i am getting this:
Request Headers Sent:
POST /api/Entries/ HTTP/1.1
Host: justalk.tukuoro.com
Accept: */*
Content-Type: text/plain
Content-Length: 179
Response Headers:
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 0
Expires: -1
Location: https://justalk.tukuoro.com/Account/Login?ReturnUrl=%2FHome%2FError
X-Powered-By: ASP.NET
Access-Control-Allow-Origin: *
Date: Mon, 16 May 2016 05:25:53 GMT
Now, it seems like it is trying to redirect me (302) but to the error page, and it is not sending anything back from the post method, which returns:
return new ObjectResult(true);
I am not sure what is going on:
Am I getting to the right controller? i have code that is supposed to log the incoming string but it does not create the log.
If it is not working? why? is the response header telling me something I don't understand?
Am I doing it right?
thank you for your time!
if you want send json post request this entryInJson must be object type not string
for example
[HttpPost]
public IActionResult Post([FromBody]EntryInJson entryInJson)
{ .... }
chat comment
the code doing the http request to my post method - how does it convert this class "RemoteEntry" to JSON data
http://www.newtonsoft.com/json/help/html/serializingjson.htm
Update :
302 is not an error but a redircet to a tempoaray location. you could resend the request to the new location received in the response header.
and it's propably a server misconfiguration issue then.
modSecurity firewall blocking your requests an making automatics redirections (302).

how to handle "OPTIONS Method" in ASP.NET MVC

My Sencha Touch app is posting a form to my asp.net-mvc-3 WebService, but instead of sending POST it's sending OPTIONS.
I'm reading a similar thread here, but I just don't know how to handle the OPTIONS method in my code.
I did try adding the [AllowAjax] attribute to my Action, however it doesn't seem to exist in MVC3.
OPTIONS /GetInTouch/CommunicateCard HTTP/1.1
Host: webservice.example.com
Referer: http://192.168.5.206/
Access-Control-Request-Method: POST
Origin: http://192.168.5.206
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.71 Safari/534.24
Access-Control-Request-Headers: X-Requested-With, Content-Type
Accept: /
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
In my ActionMethod, I'm using the following code.
public JsonpResult CommunicateCard(CommunicateCard communicateCard)
{
// Instantiate a new instance of MailMessage
MailMessage mMailMessage = new MailMessage();
// removed for security/brevity
// Set the body of the mail message
mMailMessage.Body = communicateCard.name; // THIS IS CURRENTLY BLANK :-(
// removed for security/brevity
mSmtpClient.Send(mMailMessage);
// do server side validation on form input
// if it's valid return true
// else return false
// currently returning NULL cuz I don't care at this point.
return this.Jsonp(null);
}
Turns out I had to create an ActionFilterAttribute
namespace WebService.Attributes
{
public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Response.Cache.SetNoStore();
filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Origin", "*");
string rqstMethod = HttpContext.Current.Request.Headers["Access-Control-Request-Method"];
if (rqstMethod == "OPTIONS" || rqstMethod == "POST")
{
filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Headers", "X-Requested-With, Accept, Access-Control-Allow-Origin, Content-Type");
}
base.OnActionExecuting(filterContext);
}
}
}
I solved this in a different way in MVC, and IIS. The reason I found this problem was because I wanted to POST data from client side javascript (which JSONP does not work for), and on top of that wanted to allow JSON data which sits inside the Content of the POST request.
In reality your code wants to ignore the first CORS OPTIONS request, as this is likely to be a "site wide setting", and not on a per API call setting.
First I configured IIS to send the CORS response, this can be done through IIS manager (or through web.config updates), if you use IIS then go to the site you want to add these two values:
Access-Control-Allow-Origin to "*" (for testing, for greater security you might want to restrict it to certain calling domains)
Access-Control-Allow-Headers, "Content-Type, Accept" (this is for posting JSON data)
Then I created a custom ActionFilter, which has to be applied for each controller that you want to accept POST data, which could trigger a CORS request. The custom action filter was:
public class CORSActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.HttpMethod == "OPTIONS")
{
// do nothing let IIS deal with reply!
filterContext.Result = new EmptyResult();
}
else
{
base.OnActionExecuting(filterContext);
}
}
}
Then at the start of each controller you need to apply this for add in an attribute, e.g.:
[CORSActionFilter]
public class DataSourcesController : Controller
Now I am sure there is a way to do this across your whole MVC solution (solutions welcome), but need to make a BBQ and the solution above works!
I added the following to my <system.webServer> config section:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Headers" value="Content-Type, Accept, X-Requested-With"/>
<add name="Access-Control-Allow-Methods" value="GET, POST, OPTIONS"/>
<add name="Access-Control-Allow-Origin" value="*"/>
</customHeaders>
</httpProtocol>
Just to answer the question why "OPTIONS" and not "POST", that is because the browser is implementing CORS (Cross-origin resource sharing ).
This is a two part process of sending the OPTIONS request first, then if the server replies with acceptable conditions the browser then POSTS the actual request with data / content in.
I tried all the answers here and none worked. I eventually realized that browsers will treat the pre-flight check as failed if it returns non 200. In my case, IIS was returning 404, even with the headers. This is because I had 2 attributes on my controller method - [HttpPost] and [HttpOptions]. Apparently, this is not a valid mechanism for expressing multiple verbs. I had to use this attribute instead: [AcceptVerbs(HttpVerbs.Options | HttpVerbs.Post)]
After struggling a lot, I found out the only way to handle CORS preflight request is to handle it with a pair of HttpModule and HttpHandler.
Sending the required headers is not enough. You have to handle the OPTIONS request early and not allow it to reach your controllers, because it will fail there.
The only way that I could do this was with an HttpModule.
I followed this blog post:
http://geekswithblogs.net/abhijeetp/archive/2016/06/04/adding-cors-support-for-asp.net--webapi-the-no-hassle.aspx
To summarize the work, this is the code:
namespace WebAPI.Infrastructure
{
using System;
using System.Web;
using System.Collections;
using System.Net;
public class CrossOriginModule : IHttpModule
{
public String ModuleName
{
get { return "CrossOriginModule"; }
}
public void Init(HttpApplication application)
{
application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
}
private void Application_BeginRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
CrossOriginHandler.AddCorsResponseHeaders(context);
}
public void Dispose()
{
}
}
public class CrossOriginHandler : IHttpHandler
{
#region Data Members
const string OPTIONS = "OPTIONS";
const string PUT = "PUT";
const string POST = "POST";
const string PATCH = "PATCH";
static string[] AllowedVerbs = new[] { OPTIONS, PUT, POST, PATCH };
const string Origin = "Origin";
const string AccessControlRequestMethod = "Access-Control-Request-Method";
const string AccessControlRequestHeaders = "Access-Control-Request-Headers";
const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
const string AccessControlAllowMethods = "Access-Control-Allow-Methods";
const string AccessControlAllowHeaders = "Access-Control-Allow-Headers";
const string AccessControlAllowCredentials = "Access-Control-Allow-Credentials";
const string AccessControlMaxAge = "Access-Control-Max-Age";
const string MaxAge = "86400";
#endregion
#region IHttpHandler Members
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
switch (context.Request.HttpMethod.ToUpper())
{
//Cross-Origin preflight request
case OPTIONS:
AddCorsResponseHeaders(context);
break;
default:
break;
}
}
#endregion
#region Static Methods
public static void AddCorsResponseHeaders(HttpContext context)
{
if (Array.Exists(AllowedVerbs, av => string.Compare(context.Request.HttpMethod, av, true) == 0))
{
var request = context.Request;
var response = context.Response;
var originArray = request.Headers.GetValues(Origin);
var accessControlRequestMethodArray = request.Headers.GetValues(AccessControlRequestMethod);
var accessControlRequestHeadersArray = request.Headers.GetValues(AccessControlRequestHeaders);
if (originArray != null &&
originArray.Length > 0)
response.AddHeader(AccessControlAllowOrigin, originArray[0]);
response.AddHeader(AccessControlAllowCredentials, bool.TrueString.ToLower());
if (accessControlRequestMethodArray != null &&
accessControlRequestMethodArray.Length > 0)
{
string accessControlRequestMethod = accessControlRequestMethodArray[0];
if (!string.IsNullOrEmpty(accessControlRequestMethod))
{
response.AddHeader(AccessControlAllowMethods, accessControlRequestMethod);
}
}
if (accessControlRequestHeadersArray != null &&
accessControlRequestHeadersArray.Length > 0)
{
string requestedHeaders = string.Join(", ", accessControlRequestHeadersArray);
if (!string.IsNullOrEmpty(requestedHeaders))
{
response.AddHeader(AccessControlAllowHeaders, requestedHeaders);
}
}
}
if (context.Request.HttpMethod == OPTIONS)
{
context.Response.AddHeader(AccessControlMaxAge, MaxAge);
context.Response.StatusCode = (int)HttpStatusCode.OK;
context.Response.End();
}
}
#endregion
}
}
and add them to web.config:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<remove name="WebDAVModule" />
<add name="CrossOriginModule" preCondition="managedHandler" type="WebAPI.Infrastructure.CrossOriginModule, Your_Assembly_Name" />
</modules>
<handlers>
<remove name="WebDAV"/>
<remove name="OPTIONSVerbHandler"/>
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*."
verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*."
verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*."
verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="CrossOrigin" verb="OPTIONS" path="*" type="WebAPI.Infrastructure.CrossOriginHandler, Your_Assembly_Name" />
</handlers>
<security>
<authorization>
<remove users="*" roles="" verbs=""/>
<add accessType="Allow" users="*" verbs="GET,HEAD,POST,PUT,PATCH,DELETE,DEBUG"/>
</authorization>
<requestFiltering>
<requestLimits maxAllowedContentLength="6000"/>
<verbs>
<remove verb="OPTIONS"/>
<remove verb="PUT"/>
<remove verb="PATCH"/>
<remove verb="POST"/>
<remove verb="DELETE"/>
</verbs>
</requestFiltering>
</security>
</system.webServer>
This works for Web API and MVC.

Resources