Project: ASP MVC 4 running under .net 4.0 framework:
When running an application under VS 2010 express (or deployed and running under IIS 7.5 on my local machine) the following (pseudocode) result from an action works as expected
[HttpPost]
public ActionResult PostWord(Model model)
{
....
Response.StatusCode = 400;
Return new JsonResult { data = new {fieldName = "Word", error = "Not really a word!" } };
(and I have assigned ContentType and ContentEncoding properties of the JsonResult object, with no difference in behaviour)
When the deployable is moved onto a web host (using IIS 7), firebug is telling me that the response is as expected (400) but there is no JSON in the response (ie there is no text of any kind). If I remove the line
Response.StatusCode = 400;
from the action, the JSON is perfectly formed in the response, but of course the response status code is 200 (OK), which interferes with the consuming javascript and appropriate function call.
Any thoughts on what might be going on and how to fix this? Thank you
I had this exact same problem; in order to make sure that the correct answer is not buried in the comments (as it was for me), I want to reiterate #Sprockincat's comment:
For me at least, it was indeed an issue with IIS Custom errors, and can be solved with:
Response.TrySkipIisCustomErrors = true;
#Sprockincat - you should get credit for this. I'm just making it more visible because it's such a subtle fix to a problem that is quite difficult to diagnose.
I've created a subclass of JsonResult that allows you to specify the HttpStatusCode.
public class JsonResultWithHttpStatusCode : JsonResult
{
private int _statusCode;
private string _statusDescription;
public JsonResultWithHttpStatusCode(object data, HttpStatusCode status)
{
var code = Convert.ToInt32(status);
var description = HttpWorkerRequest.GetStatusDescription(code);
Init(data, code, description);
}
public JsonResultWithHttpStatusCode(object data, int code, string description)
{
Init(data, code, description);
}
private void Init(object data, int code, string description)
{
Data = data;
_statusCode = code;
_statusDescription = description;
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.StatusCode = _statusCode;
context.HttpContext.Response.StatusDescription = _statusDescription;
base.ExecuteResult(context);
}
}
Then you can return this as your result and the status code will get set on the response. You can also test the status code on the result in your tests.
For anyone looking for this - in ASP.NET Core you can set the StatusCode property of JsonResult.
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.jsonresult.statuscode
Related
This is an attempt to use Loopj for a Synchronous put and post call from a HTTP utility class. The code uses a syncrhonous client as it’s used inside an AsyncTask and some UI interactions depend heavily on the json response so the AsyncTask is managing making the call asynchronously.
All the get calls from the HTTP utility class are working successfully. The post and put do not and they both appear to have exactly the same problem.
The json string is created using Gson. I’ve tested the json output from the application directly in Postman and it posts exactly as expected to the API, so it appears to be well formed and behaves totally as expected without any errors.
Both the put and post calls are constructed without throwing an error. Basic authorization is being added (as shown on the client instance). The SyncHTTPClient put method is called using a null context parameter. I did a bit of research and found a single post where this was being done successfully.
https://github.com/loopj/android-async-http/issues/1139
The put call fires but doesn’t enter either the overridden methods of the handler. It just returns null. I've included a portion of the working class to view:
public void executePutSave(String name, String pass, String jsonBody) {
client.setBasicAuth(name, pass);
executeLoopJPutCall("/api/Save", jsonBody);
}
public void executeLoopJPutCall(String relativeUrl, String jsonBody) {
String url = getAbsoluteUrl(relativeUrl);
StringEntity entity = new StringEntity(jsonBody, "UTF-8");
jsonResponse = null;
client.put(null, url, entity, "application/json", new JsonHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
super.onSuccess(statusCode, headers, response);
jsonResponse = response.toString();
Log.i(TAG, "onSuccess: " + jsonResponse);
}
#Override
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
super.onFailure(statusCode, headers, throwable, errorResponse);
jsonResponse = errorResponse.toString();
Log.e(TAG, "onFailure: " + statusCode + errorResponse );
}
}
);
}
So, apparently the header must be added explicitly when using the above code to Post or Put json to the API. Once I changed the header authentication line from this:
client.setBasicAuth(name, pass);
To this:
String userpass = name + ":" + pass;
String encoded = new String(Base64.encode(userpass.getBytes(),Base64.NO_WRAP));
client.addHeader("Authorization", "Basic "+encoded);
...everything worked as expected.
I found the information on this blog: https://github.com/loopj/android-async-http/issues/113
Passing a null context worked, too.
I'm having an issue with a server side validation with Razor Pages (ASP.Net Core 2.0)
I have a page that creates a record
http://localhost:56876/Entries/Create?employeeID=112345
my code behind file for the OnGet looks like:
[BindProperty]
public EntryLog EntryLog { get; set; }
public Employee Employee{ get; set; }
public string empID { get; set; }
public IActionResult OnGet(string employeeID)
{
empID = employeeID;
// for Selects for lookup tables
ViewData["ReasonId"] = new SelectList(_context.Reason, "Id", "Name");
ViewData["ReasonTypeId"] = new SelectList(_context.ReasonType, "Id", "Name");
return Page();
}
The code above works just fine and client validation works perfect but I have business logic validation that if the Entry Date is 90 between today's date and hired date that I should not let the entry to be saved.
public async Task<IActionResult> OnPostAsync()
{
//Lookup current employeeID hiredDate. if is new employee do not continue.
Employee = await _context.Employee .FirstOrDefaultAsync(p => p.Id == EntryLog.EmployeeID);
//Server side validation
var age = DateTime.Today.Day - Employee.HireDate.Day;
if (age <= 90)
{
ModelState.AddModelError("NewEmp", "Emp is New Warning");
return Page();
}
if (!ModelState.IsValid)
{
return Page();
}
_context.EntryLog.Add(EntryLog);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
My server side validation works but the problem is that when I return Page();
The form gets refreshed and the two selects elements get empty out and the EmployeeID element gets empty out as well.
My guess is that the return Page() on OnPostAsync() is not calling the OnGet() method.
How can I keep the page as it is without loosing the info?
You are right the return Page() does not call OnGet() method. I´ll try to explain what is happening:
Short Answer:
When you receive the Post request (OnPost()) and return Page() the server just returns the response (html), it does not make a new Get Request so that OnGet() get called again.
The two selects elements are being cleant because they are set up in OnGet() through ViewData (that is temporary) not in OnPost(). My suggestion is that you set the "selects" again on OnPost() (extract to a method to make it easier).
Long Answer:
When you access your Page (Entries/Create) by typing or being redirected through a link, the browser request the page to the server using HTTP METHOD GET, which will invoke OnGet method. Afterwards, when you send your form (considering method="POST") the request will be sent to the server and be caught by the OnPost method.
When you code return Page(), it is not sending back a response to browser saying "Hey, you have to make a GET request again (what would make OnGet be called again)", it is returning the end response (html) itself. That´s why OnGet() is not being called. What would make the OnGet() be called again would be return RedirectToPage("./Entities/Create"), but doing so you would lose your Model state (validation result).
The two selects elements are being cleant because they are set up in OnGet() through ViewData (that is temporary) not in OnPost(). My suggestion is that you set the "selects" again on OnPost() (extract to a method to make it easier).
Regards.
You can simply call OnGet from your OnPost method, i.e.:
if (!ModelState.IsValid) {
return OnGet();
}
This works for me in ASP.NET Core 2.2 and preserves all validation errors.
I am writing an application where among other things I need to do CRUD operations with certain objects. I need to be able to serve both HTML pages for human users, and JSON for other applications. Right now my URLs look like this for "Read":
GET /foo/{id} -> Serves HTML
GET /rest/foo/{id} -> Serves JSON
etc.
This seems a little redundant. I would rather have something like this:
GET /foo/{id}.html OR /foo/{id} -> Serves HTML
GET /foo/{id}.json -> Serves JSON
Can Spring Boot do this? If so, how?
I know how to return JSON:
#RequestMapping(value = "/foo/{id}", method = RequestMethod.GET, produces = "application/json")
public Object fetch(#PathVariable Long id) {
return ...;
}
I also know how to return HTML:
#RequestMapping("/app/{page}.html")
String index(#PathVariable String page) {
if(page == null || page.equals(""))
page = "index";
return page;
}
But I'm not sure how to have a controller do one or the other based on the request.
It's a default behavior for Spring Boot. The only thing is that you have to mark one of #RequestMapping to produce JSON. Example:
#Controller
class HelloController {
// call http://<host>/hello.json
#RequestMapping(value = "/hello", method = RequestMethod.GET, produces = "application/json")
#ResponseBody
public MyObject helloRest() {
return new MyObject("hello world");
}
// call http://<host>/hello.html or just http://<host>/hello
#RequestMapping(value = "/hello", method = RequestMethod.GET)
public String helloHtml(Model model) {
model.addAttribute("myObject", new MyObject("helloWorld"));
return "myView";
}
}
Read more at: http://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc and http://spring.io/blog/2013/06/03/content-negotiation-using-views
Actually, you are mixing rest web service with html pages, it's a bad practice.
If you want to build something really great, here is my advice.
Write only CRUD operations in your controllers and all html/css/js keep in some static folder and when you will want to see ui part - just call that static index.html file
You can read more about that here - http://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot
But if you really want to do things as it is now, here is the solution:
#RequestMapping(value = "/foo/{id}", method = RequestMethod.GET)
public Object serve(final HttpServletRequest req, final HttpServletResponse resp, #PathVariable final Long id) {
String header = req.getHeader("Accept");
// If Accept header will be text/html - then we are forwarding to jsp page.
if(header.equals("text/html")) {
req.getRequestDispatcher("login.jsp").forward(req, resp);
}
// In other cases we are returning json and setting appropriate headers.
resp.setHeader("Content-Type", "application/json");
Object object = "Some string";
return object;
}
I'm a little bit new to all of these technologies so I'll try to be as clear as I can.
I'm writing a windows phone app that sends data in string format to a server:
public class sendDataControl
{
private string response = "";
public void sendToServer(string FullSTR)
{
try
{
WebClient webClient = new WebClient();
Uri uri = new Uri("http://pricequeryserver.azurewebsites.net/api/ReceiptDataService/?incomingdata=");
webClient.UploadStringAsync(uri,FullSTR);
webClient.UploadStringCompleted += new UploadStringCompletedEventHandler(webClient_UploadStringCompleted);
}
catch (Exception ex)
...
...
}
}
void webClient_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{
if (e.Error != null)
{
responseXml=e.Error.Message;
MessageBox.Show(responseXml);
return;
}
else
{
responseXml = e.Result;
}
}
}
The server is an MVC4, basic, with api controller I added, that needs to get the data sent from the mobile.
As a test I'm just getting back a string that I send:
public class ReceiptDataServiceController : ApiController
{
private ReceiptContext db = new ReceiptContext();
...
...
public string GetDataFromMobile(string IncomingData)
{
return IncomingData;
}
}
While running the application I get an error via responseXml:
"The remote server returned an error: NotFound".
The server returns the right answer from all kinds of browsers, while on IIS and on the azure but not from the mobile emulator.
Any suggestions?
If you take a look at the documentation for UploadStringAsync overload you are using, you will notice that it sends data using POST method. While in your controller you have only implemented GET. And for your
You have to use other overload of UploadStringAsync, which lets you specify the HTTP VERB to use. And you must specify GET. Your client code should be converted to:
webClient.UploadStringAsync(uri,"GET", FullSTR);
And the best solution for simple GET operations like your is to actually use DownloadStringAsync:
var fullUri = new Uri("http://pricequeryserver.azurewebsites.net/api/ReceiptDataService/?incomingdata=" + FullStr);
webClient.DownloadStringAsync(fullUri);
Anyway, your question has nothing to do with Windows Azure, thus the removed tag.
I am looking for sending JSON object to the server via GET.
Chris's answer on Post an Array of Objects via JSON to ASP.Net MVC3 works for the http POST but not for GET.
My case also works for POST but not for GET. What can I do to make GET work
Here is my case:
in Controller I have the following method
public ActionResult Screenreport(Screentable screendata)
{
// do something here
return View();
}
I have two ModelView as follows:
public class Screenrecord
{
public string Firstname{ get; set; }
public string Lastname{ get; set; }
}
public class Screentable
{
public List<Screenrecord> Screenlist { get; set; }
}
On the client side I generate JSON object
var Screentable = { Screenlist: screendata };
screendata is an array of Screenrecord
All this work when I use POST but when I use GET I am getting null value (screendata = null) Controllers' method.
In other word when click GO, screendata is null in Screenreport(Screentable screendata) routine.
Also, if I send one JSON object it works but if I send an array (list) like I described, it does not.
Is what I am trying to do doable?
No :-)
Thats not how get works.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
(see 9.3 GET)
"The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI"
Request-URI being the important part here. There is no concept of body data in a GET request.
Try changing method to public ActionResult Screenreport(HttpRequestMessage request)
Then use below code to get JSON object.
data = request.RequestUri.Query;
data = HttpUtility.ParseQueryString(data).Get("request");
Try this example in Javascript:
var someObject = {
id:123456,
message:"my message",
}
var objStr = JSON.stringify(someObject);
var escapedObjStr = encodeURIComponent(objStr);
var getUrlStr = "http://myserver:port?json="+escapedObjStr
and now you can forward this URL to your server. I know this is not in any .NET language but you can definitely find the equivalent methods used, or just use the JS right away.