How can I conveniently conditionally set the HTTP status code in an Spring MVC request handler?
I have a request handler that responds to POST requests, for creation of a new resource. If the request is valid I want it to redirect to the URI of the new resource, returning a 201 (Created) HTTP status code. If the request is invalid I want it to give the user a chance to correct the error in the submitted form, and should not give a status code of 201.
#RequestMapping(value = { "/myURI/" }, method = RequestMethod.POST)
public String processNewThingForm(
#ModelAttribute(value = "name") final String name,
final BindingResult bindingResult) {
myValidator.validate(name, bindingResult);
if (!bindingResult.hasErrors()) {
getService().createThing(name);
return "redirect:" + name;
} else {
return "newThingView";
}
}
But that does not give the correct response status for the redirection case.
I can't simply add a #ResponseStatus, because there are two possible statuses. I'm hoping there is a neater way than manually manipulating the HttpServletResponse. And I want to indicate the view name to use, so I can not have the request handler return a ResponseEntity object with the status set appropriately.
In case you want a 201 response code, you can return a ResponseEntity with HttpStatus.CREATED when the resource is created and the view name otherwise. If so, you cannot use a redirect (http code 301). See RedirectView.
Related
I'm buildin a console Web API to communicate with a localhost server, hosting computer games and highscores for them. Every time I run my code, I get this charming error:
fail:
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.NotSupportedException: Deserialization of types without a
parameterless constructor, a singular parameterized constructor, or a
parameterized constructor annotated with 'JsonConstructorAttribute' is
not supported. Type 'System.Net.Http.HttpContent'. Path: $ |
LineNumber: 0 | BytePositionInLine: 1.
This is the method I'm using to post to the database. Note that this method is not in the console application. It is in the ASP.NET Core MvC application opening a web browser and listening for HTTP requests (which can come from the console application).
[HttpPost]
public ActionResult CreateHighscore(HttpContent requestContent)
{
string jasonHs = requestContent.ReadAsStringAsync().Result;
HighscoreDto highscoreDto = JsonConvert.DeserializeObject<HighscoreDto>(jasonHs);
var highscore = new Highscore()
{
Player = highscoreDto.Player,
DayAchieved = highscoreDto.DayAchieved,
Score = highscoreDto.Score,
GameId = highscoreDto.GameId
};
context.Highscores.Add(highscore);
context.SaveChanges();
return NoContent();
}
I'm sending POST requests in a pure C# console application, with information gathered from user input, but the result is exactly the same when using Postman for post requests - the above NotSupportedException.
private static void AddHighscore(Highscore highscore)
{
var jasonHighscore = JsonConvert.SerializeObject(highscore);
Uri uri = new Uri($"{httpClient.BaseAddress}highscores");
HttpContent requestContent = new StringContent(jasonHighscore, Encoding.UTF8, "application/json");
var response = httpClient.PostAsync(uri, requestContent);
if (response.IsCompletedSuccessfully)
{
OutputManager.ShowMessageToUser("Highscore Created");
}
else
{
OutputManager.ShowMessageToUser("Something went wrong");
}
}
I'm new to all this HTTP requests stuff, so if you spot some glaring errors in my code, that would be appreciated. Though, the most important question is, what am I missing, and how can I read from the HttpContent object, to be able to create a Highscore object to send to the database?
It seems to be the string jasonHs... line that is the problem, since the app crashed in exactly the same way, when I commented out the rest of the ActionResult method.
Based on your code, we can find that you make a HTTP Post request with a json string data (serialized from a Highscore object) from your console client to Web API backend.
And in your action method, you create an instance of Highscore manually based on received data, so why not make your action accept a Highscore type parameter, like below. Then the model binding system would help bind data to action parameter(s) automatically.
[HttpPost]
public ActionResult CreateHighscore([FromBody]Highscore highscore)
{
//...
I have a function in my #Service class that is marked with #HystrixCommand.
This method acts as a client which sends a request to another service URL and gets back a response.
What I want to do is to trigger a fallback function when the response status code is anything other than 200. It will also trigger a fallback for any other exceptions (RuntimeExceptions etc.).
I want to do this by making use of the #HystrixProperty or #HystrixCommandProperty.
I want the client to ping the URL and listen for a 200 response status and if it does not get back a 200 status within a certain time-frame I want it to fallback.
If it gets back a 200 status normally within a certain time it should not trigger the fallback.
#HystrixCommand(fallbackMethod="fallbackPerformOperation")
public Future<Object> performOperation(String requestString) throws InterruptedException
return new AsyncResult<Object>() {
#Override
public Object invoke() {
Client client = null;
WebResource webResource = null;
ClientResponse response =null;
String results = null;
try{
client = Client.create();
webResource = client.resource(URL);
client.setConnectTimeout(10000);
client.setReadTimeout(10000);
response = webResource.type("application/xml")
.post(ClientResponse.class, requestString);
} finally {
client.destroy();
webResource = null;
}
return results;
}
};
}
I specifically want to make use of the #HystrixProperty or #HystrixCommandProperty so performing a check inside the method for response status code not being 200 and then throwing an Exception is not acceptable.
Instead of using Annotations will creating my own Command by extending the HystrixCommand Interface work?
Any ideas or resources for where I can start with this are more than welcome.
I don’t understand why you don’t want to check the response http status code and throw an exception if it is not 200? Doing that will give you the behaviour you desire. i.e. it will trigger a fall back for exceptions or non 200 responses.
You can set the timeout in the client, however I would opt for using the hystrix timeout values. That way you can use Archaius to dynamically change the value at runtime if desired.
You can use the Hystrix command annotation or extend the HystrixCommand class. Both options will provide you with your desired behaviour
Here is an example using the annotation.
#HystrixCommand(fallbackMethod = "getRequestFallback")
public String performGetRequest(String uri) {
Client client = Client.create();
WebResource webResource = client.resource(uri);
ClientResponse response = webResource.get(ClientResponse.class);
if (response.getStatus() != 200) {
throw new RuntimeException("Invalid response status");
}
return response.getEntity(String.class);
}
public String getRequestFallback(String uri) {
return "Fallback Value";
}
I was trying to research the problem, but failed, therefore am asking this question here. I have an MVC application calling Web API. One of the methods returns 405 error, and I have no idea why, especially that all of the others methods in the same controller work absolutely fine.
This is how I call this method from MVC end:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(String.Format("{0}/api/Account/ArchiveAddress?addressId={1}", uri, addressId));
request.Method = "Get";
try
{
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
}
catch (Exception e)
{
return RedirectToAction("Error", "Error", new { error = e.Message });
}
Where uri is a string:
http://localhost:52599
and addressId is an int
My method on the Web API end looks like this:
[Route("ArchiveAddress")]
public IHttpActionResult ArchiveUserAddress(int addressId)
{
var address = _addressRepo.Find(addressId);
...
As I said, I call many different methods on the API in the exact same way, and they all work fine. Just this one does not want to behave. What might be causing that?
I think you need to decorate your action method (ArchiveUserAddress) with the [HttpGet] attribute or name it something that begins with Get..., e.g. GetArchiveUserAddress. As it stands, it would do match the POST method which means there's no getter hence the error you're getting.
Cookies added to the HttpServletResponse during an $.ajax POST call do not appear in the response header (there is no set-cookie). The same code does function properly during GET requests.
I have the following code in an interceptor postHandle:
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
.
.
Cookie cookie = new Cookie(User.USER_KEY, userAsJson);
LOGGER.info("Cookie json is: " + userAsJson);
cookie.setPath("/");
response.addCookie(cookie);
LOGGER.info("Header names: " + response.getHeaderNames());
LOGGER.info("Set-cookie header(s): " + response.getHeaders("Set-Cookie"));
}
I'm seeing this issue when returning from a request to this mapping:
#RequestMapping(value = "/api/user/wait", method = RequestMethod.POST)
#ResponseBody
public User waitingApi(HttpSession session) {
Ajax call parameters:
var ajaxMessage = {
url : '/api/user/wait',
type : 'POST',
success : waitCallback,
error : waitErrorCallback
};
On a GET I see the following in my logs:
Cookie json is: { my valid json object }
Header names: [Set-Cookie]
Set-cookie header(s): [user="{ my valid json object }"; Version=1;
Path=/]
On a POST I see the following in my logs:
Cookie json is: { my valid json object }
Header names: [Content-Type, Transfer-Encoding, Date, Server]
Set-cookie header(s): [] <--- this is empty, not redacted
After much time spent with google, I found this post:
http://mjremijan.blogspot.ca/2012/06/spring-not-setting-cookie-on-ajax.html
Is short, the postHandle interceptor doesn't do anything when the request hits an operation which has the annotation #ResponseBody. You can set the cookie inside the operation method by adding the response object to the operation parameters and calling addCookie inside the operation.
In both postHandle and afterCompletion methods it check whether the response is committed or not. Both scenarios are too late to add a cookie.
Set the cookie inside the preHandle and you good to go.
I want to modify HTTP request URI and HTTP request method using a CXF interceptor in a HTTP client.
I have developed something like this:
public class MyInterceptor extends AbstractPhaseInterceptor<Message> {
public MyInterceptor() {
super(Phase.PRE_PROTOCOL);
}
public void handleMessage(Message message) {
// this returns me correct path and method
// String path = (String) message.getExchange().getOutMessage().get(Message.REQUEST_URI);
// String method = (String) message.getExchange().getOutMessage().get(Message.HTTP_REQUEST_METHOD);
// this does not work as expected
String path = (String) message.get(Message.REQUEST_URI);
String method = (String) message.get(Message.HTTP_REQUEST_METHOD);
// do things here
}
}
Why do need I to use exchange/OutMessage to obtain data about current message and I can not use message directly?
How can I edit both values? I tried using message.put(<key>, <value>) and the same with exchange/OutMessage, but nothing is modified.
Coming to the path, you'd always get that value as null, I believe.
You can try following code, to get the actual value of your uri:
String requestURI = (String) message.get(Message.class.getName() + ".REQUEST_URI");