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;
}
Related
I have created WebAPI. In it's GET method I want to load local JSON file and pass it as response. So that when someone accesses this endpoint he will get said JSON response. As I'm total newb with WebAPI and JSON I don't know where to start. Even though I searched a lot through web.
I need something like this (don't know actual functions and classes):
// GET api/values
public JSON Get()
{
var json = File.Load(pathtoJSON.json);
return json;
}
As a newb you can tackle this problem in three steps.
STEP I - Go through some basic tutorial on youtube like these.
STEP II - Create s basic controller (get method) and return a simple String like "Hello World!". Try it and see if you are getting the response.
STEP III - Now you got something working then try to read JSON file and send them as response.
EDIT: if still you got issues, here is some very basic code for you reference:
using Newtonsoft.Json;
using System;
using System.IO;
using System.Web.Http;
public class ValuesController : ApiController
{
public UserData Get()
{
UserData userData = null;
using (StreamReader r = new StreamReader(#"C:\testjson.json"))
{
string json = r.ReadToEnd();
userData = JsonConvert.DeserializeObject<UserData>(json);
}
return userData;
}
public class UserData {
[JsonProperty("first_name")]
public string FirstName;
[JsonProperty("last_name")]
public string LastName;
[JsonProperty("age")]
public String Age;
}
}
and testjson.json
{
"first_name":"FirstTest1",
"last_name":"LastTest1",
"age":"25"
}
and the response
I implement a client application. This application consume a Rest webservice and these service return and html page as a variable in a model.
I take these html page successfully from Rest Service and try to write to a blank html page.
My code to write html page.
public void writeToHtml(ResponseModel response) {
FileWriter fWriter = null;
BufferedWriter writer = null;
try {
fWriter = new FileWriter(src/main/resources/templates/test.html);
writer = new BufferedWriter(fWriter);
writer.write(response.getHtmlPage());
writer.newLine();
writer.close();
} catch (Exception e) {
}
}
These function can take htmlPage from ResponseModel and write successfully to test.html
Untill there everthing work properly and my controller display it on secreen.
However, if I again call same Rest service, it can again write to "test.html" but, on the screen it shows the first created html page.
Probably it cache the first html and if I rewrite again. I just take cache one.
My Controller
#RequestMapping(value = "/testPath", method = RequestMethod.POST)
public String payment(RequestModel paymentInfoModel, BindingResult bindingResult, Model model) {
RestTemplate restTemplate = new RestTemplate();
ResponseModel response = restTemplate.postForObject(url, request, ResponseModel.class);
writeToHtml(response);
return "test";
}
Could you help me to solve these issue ?
IDEA : Inteliji
I solved my problem a bit differently:
#RequestMapping(value = "/testPath", method = RequestMethod.POST, produces = "text/html")
#ResponseBody
public String payment(RequestModel paymentInfoModel, BindingResult bindingResult, Model model) {
RestTemplate restTemplate = new RestTemplate();
ResponseModel response = restTemplate.postForObject(url, request, ResponseModel.class);
writeToHtml(response);
return response.getHtmlPage();
}
So I don't need to create an HTML page.
I would like to respond via a JsonResult from a piece of Asp.Net Core middleware but it's not obvious how to accomplish that. I have googled around alot but with little success. I can respond via a JsonResult from a global IActionFilter by setting the ActionExecutedContext.Result to the JsonResult and that's cool. But in this case I want to effectively return a JsonResult from my middleware. How can that be accomplished?
I framed the question with regard to the JsonResult IActionResult but ideally the solution would work for using any IActionResult to write the response from the middleware.
Middleware is a really low-level component of ASP.NET Core. Writing out JSON (efficiently) is implemented in the MVC repository. Specifically, in the JSON formatters component.
It basically boils down to writing JSON on the response stream. In its simplest form, it can be implemented in middleware like this:
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
// ...
public async Task Invoke(HttpContext context)
{
var result = new SomeResultObject();
var json = JsonConvert.SerializeObject(result);
await context.Response.WriteAsync(json);
}
For others that may be interested in how to return the output of a JsonResult from middleware, this is what I came up with:
public async Task Invoke(HttpContext context, IHostingEnvironment env) {
JsonResult result = new JsonResult(new { msg = "Some example message." });
RouteData routeData = context.GetRouteData();
ActionDescriptor actionDescriptor = new ActionDescriptor();
ActionContext actionContext = new ActionContext(context, routeData, actionDescriptor);
await result.ExecuteResultAsync(actionContext);
}
This approach allows a piece of middleware to return output from a JsonResult and the approach is close to being able to enable middleware to return the output from any IActionResult. To handle that more generic case the code for creating the ActionDescriptor would need improved. But taking it to this point was sufficient for my needs of returning the output of a JsonResult.
As explained by #Henk Mollema, I have also made use of Newtonsoft.Json JsonConvert class to serialize the object into JSON through SerializeObject method. For ASP.NET Core 3.1 I have used JsonConvert inside the Run method. Following solution worked for me:
Startup.cs
using Newtonsoft.Json;
// ...
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Run(async context =>
{
context.Response.StatusCode = 200;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(new
{
message = "Yay! I am a middleware"
}));
});
}
}
As of ASP.NET Core 5.0, you can use WriteAsJsonAsync to write JSON to the response stream. This method handles serialization and setting the content type header appropriately.
For example
using Microsoft.AspNetCore.Http;
// ..
public async Task InvokeAsync(HttpContext context)
{
var result = new SomeResultObject();
await context.Response.WriteAsJsonAsync(result);
}
I am trying to make method Spring MVC method in controller to return text instead of json.
My current method looks like this
#RequestMapping(value = "/upload", method = RequestMethod.POST, produces = "text/html")
public ModelAndView uploadFile(#RequestParam("file") MultipartFile file) {
LOGGER.debug("Attempt to upload file with template.");
try {
String fileContent = FileProcessUtils.processFileUploading(file);
return createSuccessResponse(fileContent);
} catch (UtilityException e) {
LOGGER.error("Failed to process file.", e.getWrappedException());
return createResponse(INTERNAL_ERROR_CODE, e.getMessage());
}
}
But the response header content-type: application/json.
I was trying to pass HttpServletResponse to controller and set content type but it still continued to return json.
What's the problem?
What's FileProcessUtils? Google doesn't bring up anything. Is it a class created by you or your organization? It would appear that the method is returning a response with a content-type of application/json. What were you expecting it to return and why? You would have to somehow parse the json to extract the data necessary for constructing a ModelAndView or find another method that returns what you want.
But without more information on FileProcessUtils, it isn't possible to provide more of an answer.
You can either do this:
#RequestMapping(value = "/foo", method = RequestMethod.GET)
public ResponseEntity foo() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_HTML);
return ResponseEntity.ok().headers(headers).body("response");
}
or do this:
#RequestMapping(value = "/foo", method = RequestMethod.GET, produces = MediaType.TEXT_HTML_VALUE)
Both works fine.
I am working with Spring MVC using JSON objects. while I am tring to send JSON Object from RESTClient, I am getting
HTTP Status 400 - The request sent by the client was syntactically incorrect ().
This is my controller
ObjectMapper mapper=new ObjectMapper();
#RequestMapping(value = "/addTask", method = RequestMethod.GET)
public ModelAndView addTask(#RequestParam("json") String json) throws JsonParseException, JsonMappingException, IOException
{
System.out.println("Json object from REST : "+json);
Task task=(Task) mapper.readValue(json, Task);
service.addService(task);
return new ModelAndView("Result");
}
My request URL : http://localhost:8080/Prime/addTask
My Json Object :
{"taskName":"nothing","taskId":1234,"taskDesc":"nothing doing"}
Also i tried specifying "Content-Type: application/json" in RESTClient but still am getting the same error
I ran into a similar situation using a JSON string in the request body recently, and using a very similar Spring setup as yours. In my case I wasn't specifying a String parameter and deserialising it myself though, I was letting Spring do that:
#RequestMapping(value = "/myService/{id}", method = RequestMethod.POST)
#ResponseBody
public void myService(#PathVariable(value = "id") Long id, #RequestBody MyJsonValueObject request) {
..
}
I was getting an HTTP error 400 "The request sent by the client was syntactically incorrect" response. Until I realised that there wasn't a default constructor on the #RequestBody MyJsonValueObject so there were problems deserialising it. That problem presented in this way though.
So if you are using POST and objects, and getting errors like this, make sure you have a default constructor! Add some JUnit to be sure you can deserialise that object.
Note: I'm not saying this is the only reason you get this error. The original case used just String (which does have a default constructor !) so it's a little different. But in both cases it appears the request URI appears to have been mapped to the right method, and something has gone wrong trying to extract parameters from the HTTP request.
Try this
Change
#RequestParam("json") String json
To
#RequestBody Task task
If you are not interested in POST method you can try this
change your Controller method from
#RequestMapping(value = "/addTask", method = RequestMethod.GET)
public ModelAndView addTask(#RequestParam("json") String json)
to
#RequestMapping(value = "/addTask/{taskName}/{taskId}/{taskDesc}", method = RequestMethod.GET)
public ModelAndView addTask(#RequestParam("taskName") String taskName,
#RequestParam("taskId") String taskId,#RequestParam("taskDesc") String taskDesc)
and change your URL to
http://localhost:8080/Prime/addTask/mytask/233/testDesc
My problem was due to the incorrect mapping of the #RequestBody object.
My Request Body looks like this
{data: ["1","2","3"]}
I had the following code in my controller
#RequestMapping(value = "/mentee", method = RequestMethod.POST)
public #ResponseBody boolean updateData(#RequestBody List<Integer> objDTO, HttpSession session) {
...
}
This give me HTTP 400 because Spring doesn't know how to bind my Json data to a List.
I changed the RequestBody object to the following
#RequestMapping(value = "/mentee", method = RequestMethod.POST)
public #ResponseBody boolean updateData(#RequestBody ObjectiveDto objDTO, HttpSession session) {
...
}
and defined ObjectiveDto as followed
#ToString
public class ObjectiveDto {
#Getter #Setter
private List<Integer> data;
}
This resolved the HTTP 400 error.