I have an API controller (.NET Framework 4.8 and IIS server) that returns a file or 404 if the file is not found. I added some custom headers to the response just to specify what was the problem (I'm than using this information on FE app to show some specifying messages to user).
However, when I call the API, I don't get the custom headers when returning 404. I tried changing the status code to 200, 204 and it works.
The controller method looks like this:
[HttpPost]
public ActionResult FileDownload(DataModel data)
{
try
{
return DownloadFile(data);
}
catch
{
}
Response.Headers.Add("x-custom-header", "custom header content");
return new HttpStatusCodeResult(404);
}
The FE method for fetching the file:
fetch(props.href)
.then((response) => {
let customHeader= response.headers.get("x-custom-header");
console.log(customHeader);
if (response.status !== 200) {
throw response.status;
}
return response.blob();
})
.then((blob: Blob) => {
let fileUrl: string = window.URL.createObjectURL(blob);
let a: HTMLAnchorElement = document.createElement("a");
a.href = fileUrl;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
setLinkText(linkTextSuccess);
})
.catch((error) => {
console.log(error);
setLinkText(linkTextError);
});
Why is it that with 404 status code, the headers are not sent? Could I somehow enforce that?
Thank you for your time!
Related
I am working on project in which I want to add a value to an Oracle database and get value from it. I am using Angular 4 using VSCode and ASP.NET Web API using Visual Studio 2015 for services.
My Angular code is working well but it's not hitting the required method in the ASP.NET Web API while calling.
My Angular service code is
register(userInfo: any) {
debugger;
var url1 = this.baseUrl;
url1 += "api/signUp/SaveCustomer";
return this._http.post(url1,userInfo);
};
here base url is BASE_API_URL: "http://localhost:6705/.
My ASP.NET Web API service method is
[HttpPost]
public IHttpActionResult SaveCustomer( UserInfo user )
{
// int result;
// result = _customerManager.SaveCustomer(user);
// if (result > 0)
// {
// return Ok(result);
// }
// else
// {
// return NotFound();
// }
return null;
}
and its controller constructor is
public signUpController()
{
_customerManager = new CustomerManager();
}
When I run the project, I have put debugger at two places one at constructor and other at method Register. My control comes to constructor but its not hit the required method. I am really worried I am new to this technology. No error is showing on screen.
Use [FromBody] in action like this:
[HttpPost]
public IHttpActionResult SaveCustomer([FromBody] UserInfo user)
{
...
}
If you call Web API post method from Angular 2 type script, don't forget to add following header content and parameter object.
register(userInfo: any) {
var params = new URLSearchParams();
params.set('username', userInfo.username);
params.set('password', userInfo.password);
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
var url1 = this.baseUrl;
url1 += "api/signUp/SaveCustomer";
return this._http.post(url1, params.toString(), { headers: headers })
};
Update
register(userInfo: any) {
//let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
let headers = new Headers({ 'Content-Type': 'application/json' });
var url1 = this.baseUrl;
url1 += "api/signUp";
console.log(url1);
return this._http.post(url1, userInfo, { headers: headers });
};
Routes code below:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
Controller code below :
// POST api/values
[HttpPost]
public void Post([FromBody]Employee employee)
{
employeeManager.CreateAsync(employee);
}
All other methods working except the post method.
call from angular component :
onSubmit(employeeItems: any) {
console.log(employeeItems);
this.getData();
var headers = new Headers();
headers.append('Content-Type', 'application/json; charset=utf-8');
this.http.post('api/Employee/Post', employeeItems, { headers: headers }).subscribe();
this.createEmployeeFlag = false;
}
I tried even from Postman, but no luck.
Your url and route templates do not match
[Route("api/[controller]")]
public class EmployeeController : Controller {
[HttpPost]
public async Task<IActionResult> Post([FromBody]Employee employee) {
await employeeManager.CreateAsync(employee);
return Ok();
}
}
and update your calling URL to call the default endpoint api/Employee
onSubmit(employeeItems: any) {
console.log(employeeItems);
this.getData();
var headers = new Headers();
headers.append('Content-Type', 'application/json; charset=utf-8');
this.http.post('api/Employee', employeeItems, { headers: headers }).subscribe();
this.createEmployeeFlag = false;
}
This is the code that you would need in your service, there are two issues here, first is the URL, it needs to be the complete URL path. The second is is that you are trying to subscribe to something before mapping it to a Observable
onSubmit(employeeItems: any) {
let url: string = 'http://localhost/api/employee'; //this will be the complete url that you would hit with say postman
this.getData(); //I'm not sure what this is so I'm leaving it here
this.http.post(url, employeeItems)
.map((response: Response) => response.json())
.Subscribe((response: any) => {
//do whatever with the response here.
});
this.createEmployeeFlag = false;
}
I would suggest breaking this up into a *.service.ts file.
*.service.ts
public postEmployee(employeeItems: any): Observable<any> {
let url: string = 'http://localhost/api/employee'; //this will be the complete url that you would hit with say postman
this.http.post(url, employeeItems)
.map((response: Response) => response.json());
}
inside your *.component.ts
constructor(private service: Service) {}
onSubmit(employeeItems: any) {
this.getData(); //I'm not sure what this is so I'm leaving it here
this.service.postEmployee(employeeItems)
.Subscribe((response: any) => {
//do whatever with the response here.
});
this.createEmployeeFlag = false;
}
im experiencing a weird problem.
I work with MVC (not web api), so the controller inherits from Controller, not from ApiController.
Im calling a controller action (POST) with ajax, and the action is returning HttpResponseMessage
This is the response i get:
{"readyState":4,"responseText":"StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StringContent, Headers:\r\n{\r\n Location: /\r\n Content-Type: application/json; charset=utf-8\r\n}","status":200,"statusText":"OK"}
However, when ajax receives the data, it fires the fail method.
This is the ajax function:
$.ajax({
url: "someurl",
type: "post",
data: data,
dataType: "json",
contentType: "application/json; charset=utf-8"
}).done(function (data) {
alert(data.responseText);
window.location.href = "redirect to some url";
}).fail(function (data) {
alert("error");<-- this one is called even when i set HttpStatusCode.OK
alert(JSON.stringify(data));
}).always(function () {
});
This is a simplified controller action:
[HttpPost]
[AllowAnonymous]
public HttpResponseMessage Index(HttpRequestMessage request, Login model)
//public HttpResponseMessage Index(Login model) // i get the same with any of these 2
{
HttpResponseMessage response = new HttpResponseMessage();
string message = "";
if (something)
{
response.StatusCode = HttpStatusCode.OK;
FormsAuthentication.SetAuthCookie(model.UserName, true);
currentUser.LastLoginDate = DateTime.Now;
currentUser.FailedPasswordAttemptCount = 0;
ModelRepositories.MobileCommerceRepository.SaveChanges();
}
else
{
message = "User does not exist. The user name or password provided is incorrect.";
response.StatusCode = HttpStatusCode.BadRequest;
}
//response = request.CreateResponse(HttpStatusCode.OK);
string json = JsonSerializer.SerializeToString(message);
response.Content = new StringContent(json, Encoding.UTF8, "application/json");
return response;
}
If i do the same ajax call to web api controller instead (with the same C# code inside), it fires success. What is the difference ?
Thanks
You cannot use HttpResponseMessage with an MVC action. Web API and MVC are two different frameworks, you can't mix and match pieces of them.
Just putting my answer if anyone is still looking. I agree with previous post as MVC will not return actual content. Instead I used
public async Task<string> GetAjaxCollateralSummary(string id)
{
//your logic
return JsonConvert.SerializeObject(result);
}
and the in my js page I have following code:
$.post(url, { id: '2' })
.success(function (response) {
console.log(response);
var jsondResult = JSON.parse(response);
}
)
.error(function (err) {
console.log("Error while trying to get data from MVC controller ");
console.log(err);
})
I'd like some guidance in returning an XML doc from a controller to a view.
In my view, I'd like to traverse the XML doc using JQuery. There are plenty of online examples using JQuery for this use.
I have a PortfolioList() Controller below, which right now just returns the view, but I would like to figure out how to RETURN THE XML RESPONSE. You'll noticed below that I'm writing the XML response to a local file just for testing purposes...
Do I need to cleanly create a model for this ?
public ActionResult PortfolioList()
{
XmlDocument xmlResponse = new XmlDocument();
XmlDocument xmlRequest = new XmlDocument();
bool rzInitialized = nitializeRz();
if (rzInitialized == false)
{
ViewBag.Message = "Rz Init has failed. Check if Rz is running";
return View();
}
bool rzConnected = ConnectToRz();
ViewBag.Message = "Here you may view a list of portfolios and exposures.";
// Build Portfolio Select request here !
RequestBuilder rzRequest = new RequestBuilder();
// REQUEST FOR PORTFOLIOS !
string portfoliosRequest = rzRequest.PortfoliosRequest("Portfolios");
string **portfoliosResponse** = RzClient.sendRequest(portfoliosRequest, false);
// DEBUG REQUESTS !!
if (Debugflag)
{
rzRequest.DebugOutput("portfolios", portfoliosRequest, portfoliosResponse);
}
DisconnectFromRz();
return View("PortfolioList");
}
You can do it as follows.
public ActionResult PortfolioList()
{
//Your code
....
return this.Content(yourXml, "text/xml");
}
If returning xml document from controller action is all you about a better idea is creating a custom action result.
public class XmlDocumentResult: ContentResult
{
public XmlDocument XmlDocument { get; set; }
public override void ExecuteResult(ControllerContext context)
{
if (XmlDocument == null)
return;
Content = XmlDocument.InnerXml;
ContentType = "text/xml";
base.ExecuteResult(context);
}
}
Now you can return xml from an action as,
public XmlDocumentResult GetXml()
{
var xmlDoc = new XmlDocument();
...
return new XmlDocumentResult { XmlDocument = xmlDoc };
}
Based on another developer's advice, I'm going the Json data format route. It turns out that returning an XML doc from an asp.net Controller back to a View is a COMPLETE nightmare (i.e. I can return the XML doc itself to the browser, but I cannot figure out how to use jQuery to process the xml nodes).
I've gone the route of deserializing the XML doc on the server side, and returning a JsonResult to my View (i.e. using JQuery's Ajax routine to call into my controller).
sample XML serialization code: http://msdn.microsoft.com/en-us/library/58a18dwa.aspx#Y0
I found a jQuery code sample online which works for me !
The code sample parses an xml document as follows (the url is http://www.switchonthecode.com/tutorials/xml-parsing-with-jquery ):
<script type="text/javascript">
$(document).ready(function () {
$.ajax({
type: "GET",
url: "/Xml/xml_test1.xml",
dataType: "xml",
success: parseXml,
error: function (error) {
alert("Some problem.");
}
});
});
function parseXml(xml) {
//find every Tutorial and print the author
$(xml).find("Tutorial").each(function () {
$("#output").append($(this).find("Title").text() + "<br/>");
$(this).find("Category").each(function () {
$("#output").append($(this).text() + "<br />");
});
$("#output").append("<br/>");
});
}
However, I do not understand something like this does NOT work (but rather just dumps the entire innerText of every single element onto my page)...sorry about the commented lines:
//$.ajax({
// url: "/Portfolios/getPortfolios",
// type: "POST",
// dataType: "XML",
// async: true,
// success: function (data) {
// if (!data)
// alert("No xml data returned.");
// else {
// var $xml = $(data);
// $xml.find("portfolioSummary").each(function () {
// $('.XmlResp').text("DUDE!"); // text($(this).text());
// });
// //alert($xml.text());
// $('.XmlResp').text("Wow are we getting somewhere ?!!!");
// $('.XmlResp').replaceWith($xml.text());
// }
// },
// error: function (error) {
// alert("failed");
// }
//});
In an effort to make a progress reporting process a little more reliable and decouple it from the request/response, I am performing the processing in a Windows Service and persisting the intended response to a file. When the client starts polling for updates, the intention is that the controller returns the contents of the file, whatever they are, as a JSON string.
The contents of the file are pre-serialized to JSON. This is to ensure that there is nothing standing in the way of the response. No processing needs to happen (short of reading the file contents into a string and returning it) to get the response.
I initially though this would be fairly simple, but it is not turning out to be the case.
Currently my controller method looks thusly:
Controller
Updated
[HttpPost]
public JsonResult UpdateBatchSearchMembers()
{
string path = Properties.Settings.Default.ResponsePath;
string returntext;
if (!System.IO.File.Exists(path))
returntext = Properties.Settings.Default.EmptyBatchSearchUpdate;
else
returntext = System.IO.File.ReadAllText(path);
return this.Json(returntext);
}
And Fiddler is returning this as the raw response
HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Mon, 19 Mar 2012 20:30:05 GMT
X-AspNet-Version: 4.0.30319
X-AspNetMvc-Version: 3.0
Cache-Control: private
Content-Type: application/json; charset=utf-8
Content-Length: 81
Connection: Close
"{\"StopPolling\":false,\"BatchSearchProgressReports\":[],\"MemberStatuses\":[]}"
AJAX
Updated
The following will likely be changed later, but for now this was working when I was generating the response class and returning it as JSON like a normal person.
this.CheckForUpdate = function () {
var parent = this;
if (this.BatchSearchId != null && WorkflowState.SelectedSearchList != "") {
showAjaxLoader = false;
if (progressPending != true) {
progressPending = true;
$.ajax({
url: WorkflowState.UpdateBatchLink + "?SearchListID=" + WorkflowState.SelectedSearchList,
type: 'POST',
contentType: 'application/json; charset=utf-8',
cache: false,
success: function (data) {
for (var i = 0; i < data.MemberStatuses.length; i++) {
var response = data.MemberStatuses[i];
parent.UpdateCellStatus(response);
}
if (data.StopPolling = true) {
parent.StopPullingForUpdates();
}
showAjaxLoader = true;
}
});
progressPending = false;
}
}
The issue, I believe, is that the Json action result is intended to take an object (your model) and create an HTTP response with content as the JSON-formatted data from your model object.
What you are passing to the controller's Json method, though, is a JSON-formatted string object, so it is "serializing" the string object to JSON, which is why the content of the HTTP response is surrounded by double-quotes (I'm assuming that is the problem).
I think you can look into using the Content action result as an alternative to the Json action result, since you essentially already have the raw content for the HTTP response available.
return this.Content(returntext, "application/json");
// not sure off-hand if you should also specify "charset=utf-8" here,
// or if that is done automatically
Another alternative would be to deserialize the JSON result from the service into an object and then pass that object to the controller's Json method, but the disadvantage there is that you would be de-serializing and then re-serializing the data, which may be unnecessary for your purposes.
You just need to return standard ContentResult and set ContentType to "application/json".
You can create custom ActionResult for it:
public class JsonStringResult : ContentResult
{
public JsonStringResult(string json)
{
Content = json;
ContentType = "application/json";
}
}
And then return it's instance:
[HttpPost]
public ActionResult UpdateBatchSearchMembers()
{
string returntext;
if (!System.IO.File.Exists(path))
returntext = Properties.Settings.Default.EmptyBatchSearchUpdate;
else
returntext = Properties.Settings.Default.ResponsePath;
return new JsonStringResult(returntext);
}
Yeah that's it without no further issues, to avoid raw string json this is it.
public ActionResult GetJson()
{
var json = System.IO.File.ReadAllText(
Server.MapPath(#"~/App_Data/content.json"));
return new ContentResult
{
Content = json,
ContentType = "application/json",
ContentEncoding = Encoding.UTF8
};
}
NOTE: please note that method return type of JsonResult is not working for me, since JsonResult and ContentResult both inherit ActionResult but there is no relationship between them.
Use the following code in your controller:
return Json(new { success = string }, JsonRequestBehavior.AllowGet);
and in JavaScript:
success: function (data) {
var response = data.success;
....
}
All answers here provide good and working code. But someone would be dissatisfied that they all use ContentType as return type and not JsonResult.
Unfortunately JsonResult is using JavaScriptSerializer without option to disable it. The best way to get around this is to inherit JsonResult.
I copied most of the code from original JsonResult and created JsonStringResult class that returns passed string as application/json. Code for this class is below
public class JsonStringResult : JsonResult
{
public JsonStringResult(string data)
{
JsonRequestBehavior = JsonRequestBehavior.DenyGet;
Data = data;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if (JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("Get request is not allowed!");
}
HttpResponseBase response = context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType))
{
response.ContentType = ContentType;
}
else
{
response.ContentType = "application/json";
}
if (ContentEncoding != null)
{
response.ContentEncoding = ContentEncoding;
}
if (Data != null)
{
response.Write(Data);
}
}
}
Example usage:
var json = JsonConvert.SerializeObject(data);
return new JsonStringResult(json);