WebAPI BadRequest Dropping Response Data - angularjs

I have an ASP.NET WebAPI (v2) controller action that has the following general structure:
[HttpPost]
public HttpResponseMessage Post(UserDTO model)
{
try {
// do something
}
catch (Exception ex)
{
var error = new {
errorMessage = ex.Message,
userId = 123,
// some other simple data
};
return Request.CreateResponse(HttpStatusCode.BadRequest, error);
}
return Request.CreateResponse(HttpStatusCode.OK, model);
}
When I run this on my local development server (IIS Express) and an error is thrown, I get the expected JSON payload back.
{
config: {...},
data: {
errorMessage: "User invalid",
userId: 123,
...
},
status: 400,
statusText: "Bad Request"
}
When I run the same code/data on the remote/production server (IIS 8.5), all I get back is:
{
config: {...},
data: "Bad Request,
status: 400,
statusText: "Bad Request"
}
The custom data payload is lost/stripped away from the response. This appears to be related to the HttpStatusCode used in the Request.CreateResponse() call as if I change HttpStatusCode.BadRequest to HttpStatusCode.OK then the custom data payload is downloaded.
As I test, I tried changing the return to Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState); but the results were the same, i.e. the data was returned as a simple "Bad Request" string.
For reference, the API is being called by an AngularJS $http.post() call.
Why is the change in HttpStatusCode changing the response payload on the production server but not locally? Any help would be much appreciated.

It turns out this was down to the following section in Web.config
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="403" />
<error statusCode="403" responseMode="ExecuteURL"
path="/Error/AccessDenied" />
<remove statusCode="404" />
<error statusCode="404" responseMode="ExecuteURL"
path="/Error/NotFound" />
<remove statusCode="500" />
<error statusCode="500" responseMode="ExecuteURL"
path="/Error/ApplicationError" />
</httpErrors>
</system.webServer>
It was 'odd' because these pages were not being returned by the API call, but with their removal, the API call returned the correct payload - presumably the HttpStatusCode.BadRequest was being intercepted by an error handler somewhere hence losing the original response data?
With these handlers I removed, I resorted to using the Application_Error handler in Global.asax as described by ubik404 here.
There may well be a better/alternative way to achieve the same result, but this seems to work.

You need to remove existingResponse="Replace", I just had the same issue and that solved it for me. The default value is Auto.
Your answer lead me to find the real problem, so thanks! :)
Documentation

Related

Having problems with axios.post while try to get data FROM server

I am trying to use axios.post (in TS) to get responses from server (using POST as GET) without sending any Data. The server sends back the Data but REACT cant handle the responses.
Here is the react component:
interface allComapnies {
comapniesData:CompanyData[];
}
function GetAllCompanies(props:allComapnies): JSX.Element {
const myURL = globals.urls.admin+"get_company/all";
const [comapniesData,setData] = useState([new CompanyData()]);
useEffect(()=>{axios.post(myURL).then((response)=>{
console.log(response.data);
setData(response.data);
}).catch(error=>{console.log(error)});
},[]);
return (
<div className="getAllCompanies">
{comapniesData.map(item=><OneCompany
key ={item.id}
id={item.id}
name={item.name}
email={item.email}
/>)}
</div>
);
}
export default GetAllCompanies;
The console message shows:
Error: Request failed with status code 302
at createError (createError.js:16)
at settle (settle.js:17)
at XMLHttpRequest.onloadend (xhr.js:54)
The browser get the responses from the server:
[{id: 2, name: "company2", email: "hgfytj#fdgreg", password: "trjyjytk",…},…]
The function of the REST Post inside the Controller in the Server(SPRING):
#RestController
#RequestMapping("admin")
#CrossOrigin(origins = "localhost:3000", allowedHeaders = "*")
#RequiredArgsConstructor
public class AdminController extends ClientController {
private final AdminService adminService;
...
#PostMapping("get_company/all")
public ResponseEntity<?> getAllCompanies() {
return new ResponseEntity<>(adminService.getAllCompanies(), HttpStatus.FOUND);
}
...
}
I found the problem. It was that I sent http status number 302 as a send confirmation, instead of number 200.
This prevented the server from sending the desired information in response. I have now changed the status to 200 and the information is received even when sending "axios.post" with an empty request.
#PostMapping("get_company/all")
public ResponseEntity<?> getAllCompanies() {
return new ResponseEntity<>(adminService.getAllCompanies(), HttpStatus.OK);
}
Try GET method instead POST cause you are getting data from your DB and not sending it to.

How to handle api errors using aws-amplify?

I'm currently trying to POST data to my aws lambda functions triggered by aws api-gateway using the aws-amplify react lib.
Here is the code :
API.post("snippets","snippets/", {
body: data,
}).then(response => response).catch(console.log(err))
In the main case, everything is OK.
But my lambda function is design to validate the input data and return a status code 400 with a returned payload looking like that :
{
"errors": [
{
"field": "title",
"message": "This field is required"
}
]
}
I would like to catch those errors in order to display them in the frontend but aws-amplify seems to have an undocumented behavior.
By default, status code 400 returned are throw with a default error message :
Error: Request failed with status code 400
at createError (createError.js:16)
at settle (settle.js:18)
at XMLHttpRequest.handleLoad (xhr.js:77)
Is there a way to get the returned payload instead of this magical error?
It turns out that under the hood, aws-amplifyuse Axios to make http calls.
When using Axios, you have to console.log(error.response): https://github.com/axios/axios/issues/960
Here is the fix I've made :
API.post("snippets","snippets/", {
body: data,
}).then(response => response).catch(error => console.log(error.response.data))
A Pull Request on the aws-amplify documentation is open : https://github.com/aws/aws-amplify/pull/633
I also faced the similar issues, It showed the default error message "Request failed with status code 400", instead of the message that is returned from API.
I logged the Error object and it did not show the response attribute in it. But we do have response attribute. I tried logging the Error.response and it did contain the response sent from the API.
Just figured out this by going through the 'Cancel API requests' Amplify docs.
From what I can see this is the contents of the error object returned by the API call:
Heres what I am doing to just print out the error, obviously you would do a lot more here but its a good start.
async uploadUser(state, payload) {
const promise = API.graphql({
query: createUser,
variables: { input: payload },
});
try {
await promise;
} catch (error) {
// Print out the actual error given back to us.
console.log(error.errors[0].message);
// If the error is because the request was cancelled we can confirm here.
if (API.isCancel(error)) {
// handle user cancellation logic.
console.log(error.message);
}
}
Hope that helps 😃

Rest API is not always called

I have a Single Page Application with a webClient and a webAPI. When I go to a view which has a table, my table is not being updated. Actually the API is only being called once upon startup of application even though it is suppose to be called each time, or what I expected to happen.
Service Code -
function getPagedResource(baseResource, pageIndex, pageSize) {
var resource = baseResource;
resource += (arguments.length == 3) ? buildPagingUri(pageIndex, pageSize) : '';
return $http.get(serviceBase + resource).then(function (response) {
var accounts = response.data;
extendAccounts(accounts);
return {
totalRecords: parseInt(response.headers('X-InlineCount')),
results: accounts
};
});
}
factory.getAccountsSummary = function (pageIndex, pageSize) {
return getPagedResource('getAccounts', pageIndex, pageSize);
};
API Controller -
[Route("getAccounts")]
[EnableQuery]
public HttpResponseMessage GetAccounts()
{
int totalRecords;
var accountsSummary = AccountRepository.GetAllAccounts(out totalRecords);
HttpContext.Current.Response.Headers.Add("X-InlineCount", totalRecords.ToString());
return Request.CreateResponse(HttpStatusCode.OK, accountsSummary);
}
I can trace it to the service, but it will not hit a break point in the controller.
I added this to my web.config file for the REST API project and now it works as I need it -
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Cache-Control" value="no-cache, no-store, must-revalidate" />
<!-- HTTP 1.1. -->
<add name="Pragma" value="no-cache" />
<!-- HTTP 1.0. -->
<add name="Expires" value="0" />
<!-- Proxies. -->
</customHeaders>
</httpProtocol>
</system.webServer>
Thanks everybody for pointing me in the right direction!
I suspect the REST service response is getting cached in your browser on first call. So on REST service side add headers in response not to cache it or in your request add some additional changeable parameter(like time stamp) to ensure browser will not pick up the response form cache.

415 (Unsupported Media Type) in $http.post method

I'm quite new to REST and AngularJS, but after several hours of googling I couldn't find any answer to my question:
I'm trying to do a POST request from my angularjs frontend to my backend implemented in java (using JPA).
When I'm trying to create a json-object and to do a POST I always get the 415 (Unsupported Media Type) error.
(Actually I don't even get "into" the scope of the service (i.E. "IN SERVICE" doesn't get printed to the console)..
If I add postData.toJSON(), it actually gets "POSTed", but arrives null ...
how do I have to format my 'postData' in Order to succesfully get POSTed?
(I also tried to write the Date-properties without ' " ' - no luck...)
Thank you for your help!
FrontEnd:
app.controller('WorkController', function($scope, $http) {
$scope.saveWork = function () {
var postData = {
"status" : "OPEN",
"startDate": "1338364250000",
"endDate": "1336364253400",
"WorkText" : "Test"
};
$http.post("http://localhost:8080/service/v1/saveWork", postData)
.success(function(data, status, headers, config){
console.log("IN SAVE WORK - SUCCESS");
console.log(status);
})
.error(function(){
console.log("ERROR IN SAVE WORK!");
})
}
});
Service:
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response save(WorkDto wo){
System.out.println("IN SERVICE");
if(ass == null){
System.out.println("Could nor persist work- null");
return Response.noContent().build();
} else{
Work workDao = WorkTransformator.transform(wo);
workDao.persist();
return Response.ok().build();
}
}
Instead of building and sending a parsed JSON object, create a javascript object and send that in your post body. You can reuse your postData object, but try removing the "" surrounding properties names.
Try this:
var postData = {
status : "OPEN",
startDate: "1338364250000",
endDate: "1336364253400",
workText : "Test"
};
UPDATE
Looks like the above doesn't work by itself. I thought that the Content-Type would be infered.
Can you try to do the post request this way :
$http({
method: 'POST',
url: 'http://localhost:8080/service/v1/saveWork',
data: postData,
headers: {
'Content-Type': 'application/json'
}}); // complete with your success and error handlers...
// the purpose is to try to do the post request explicitly
// declaring the Content-Type you want to send.
UPDATE 2
If this didn't work, compose a post request using Fiddler, and check what's the response.
Here's some pointers:
Download Fiddler2 if you dont already have it
Compose a request like in the screenshot below
You can then check on the pane on the left for what was the server response code. Double click that line (Ignore the error code on the screenshot...you should be getting a 415)
After double-clicking the response line, you can check and browse for more details on the right pane:
If you can successfuly post with a «manufactured» JSON object then the problem resides on your Angular code. If not, it's certainly something wrong with your Rest Service configuration.
You can also inspect the details of your POSTS made with the Angular app in Fiddler2. That should give you a good insight of what's going on.
If you're into it, you can then update your question with some screenshots of your Angular app requests. That will certainly help us to help you :)
I finally managed to find the cause of my error!
In my Rest-Service, I directly expected my java-class as parameter. (I thought this would be parsed/deserialized automatically). Quite naive I think... :)
In order to get it working I had to:
-Expect a String as Parameter in my #POST service
-Deserialize it (using GSON)
Here is the (now working) service:
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response save(String wo){
if(wo == null){
System.out.println("Could nor persist work- null");
return Response.noContent().build();
} else{
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HHmm:ssZ").create();
WorkDto dto = gson.fromJson(wo, WorkDto.class);
Work workDao = WorkTransformator.transform(dto);
workDao.persist();
return Response.ok().build();
}
}
Thanks again António for your help!

PUT Method errors out when model string has value with webapi

I have this webapi method here:
// PUT api/Competitions/5
public HttpResponseMessage PutCompetitor(int id, CompetitionViewModel competitorviewmodel)
{
...
}
The CompetitionViewModel looks something like this:
public class CompetitionViewModel
{
public int CompetitorId { get; set; }
public string Owned{ get; set; }
public bool Sold { get; set; }
}
I have an angular http.put call to update a competition model that looks like this:
$scope.updateProject = function () {
$http.put(mvc.base + "API/Projects/" + masterScopeTracker.ProjectID, $scope.ProjectCRUD)
.success(function (result) {
})
.error(function (data, status, headers, config) {
masterScopeTracker.autoSaveFail;
});
}
On page load, a new competition is created. So I have a model like the following:
{
CompetitorId: 56,
Owned: null,
Sold: false
}
Every 15 seconds a call to update the model is made. If I don't change any of the values of the model, the webapi put method gets called and runs successfully without a problem. If I change the model to this:
{
CompetitorId: 56,
Owned: "Value",
Sold: false
}
I get a 500 Error and the method is not hit. Not understanding what I am doing wrong here. The view model accepts a string. A string is being sent in the payload. Yet I get the error. Anyone have any ideas?
UPDATE:
I was able to get the server to give me this error:
{"Message":"Anerrorhasoccurred.",
"ExceptionMessage":"Objectreferencenotsettoaninstanceofanobject.",
"ExceptionType":"System.NullReferenceException",
"StackTrace":"atClientVisit.Models.ClientVisitEntities.SaveChanges()\r\natClientVisit.Controllers.API.CompetitionsController.PutCompetitor(Int32id,CompetitionViewModelcompetitorviewmodel)\r\natlambda_method(Closure,Object,Object[])\r\natSystem.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass13.<GetExecutor>b__c(Objectinstance,Object[]methodParameters)\r\natSystem.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Objectinstance,Object[]arguments)\r\natSystem.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1func,CancellationTokencancellationToken)"
}
I should also say that this doesn't happen locally. This only happens when deployed on the clients server.
You should check the event log to see what the actual error is on the server side. I've had trouble with IIS/IIS Express before with Put because WebDAV was enabled. You can disable it in your web.config:
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true">
<remove name="WebDAVModule" />
</modules>
<handlers>
<remove name="WebDAV" />
</handlers>
</system.webServer>

Resources