Spring Boot .html files not resolving - html

I have a spring boot app -
I have a controller -
#Controller("CWOController")
#RequestMapping(value = "/cworequest")
public class CWOController {
#RequestMapping(value = {"/index.html"}, method = {RequestMethod.GET})
public String getCwoIdSearchForm(Model model) {
model.addAttribute("cwoRequestForm", new CWORequest());
return "index";
}
This works on the url -> http://localhost:8080/cworequest/
but I need -> http://localhost:8080/cworequest/index.html to map to the above GET.
How do I do that?

Add another default method with mapping / and return error or anything else.

Related

Springboot Thymeleaf Cache page

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.

How to send POST request by Spring cloud Feign

It's my Feign interface
#FeignClient(
name="mpi",
url="${mpi.url}",
configuration = FeignSimpleEncoderConfig.class
)
public interface MpiClient {
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<String> getPAReq(#QueryMap Map<String, String> queryMap
);
}
and my custom configuration
public class FeignSimpleEncoderConfig {
public static final int FIVE_SECONDS = 5000;
#Bean
public Logger.Level feignLogger() {
return Logger.Level.FULL;
}
#Bean
public Request.Options options() {
return new Request.Options(FIVE_SECONDS, FIVE_SECONDS);
}
#Bean
#Scope("prototype")
public Feign.Builder feignBuilder() {
return Feign.builder()
.encoder(new FormEncoder());
}
}
If I send request like this I see that my request send Content-Type: application/json;charset=UTF-8.
But if I set content type
consumes = "application/x-www-form-urlencoded"
I've this error message
feign.codec.EncodeException: Could not write request: no suitable HttpMessageConverter found for request type [java.util.HashMap] and content type [application/x-www-form-urlencoded]
at org.springframework.cloud.netflix.feign.support.SpringEncoder.encode(SpringEncoder.java:108) ~[spring-cloud-netflix-core-1.1.7.RELEASE.jar:1.1.7.RELEASE]
How to send POST request, I think I should make something more with Encoder.
Thanks for your help.
First of all you should change your Feign interface like this:
#FeignClient (
configuration = FeignSimpleEncoderConfig.class
)
public interface MpiClient {
#RequestMapping(method = RequestMethod.POST)
ResponseEntity<String> getPAReq(Map<String, ?> queryMap);
}
Then you should set the encoder during feign configuration:
public class FeignSimpleEncoderConfig {
#Bean
public Encoder encoder() {
return new FormEncoder();
}
}
It seems to me that Map is not valid for form body. MultiValueMap works just fine.
Feign client:
#FeignClient(name = "name", url="url", configuration = FromUrlEncodedClientConfiguration.class)
public interface PayPalFeignClient {
#RequestMapping(value = "/", method = POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
#Headers("Content-Type: application/x-www-form-urlencoded")
String foo(MultiValueMap<String, ?> formParams);
}
Config:
#Configuration
public class FromUrlEncodedClientConfiguration {
#Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
#Bean
#Primary
#Scope(SCOPE_PROTOTYPE)
Encoder feignFormEncoder() {
return new FormEncoder(new SpringEncoder(this.messageConverters));
}
}
Gradle dependencies:
compile group: 'io.github.openfeign.form', name: 'feign-form', version: '2.0.2'
compile group: 'io.github.openfeign.form', name: 'feign-form-spring', version: '2.0.5'
After that all you have to do is call it with a MultivalueMap parameter.
Specify the correct encoder for handle form encoded request
you can specify multi encoder example json/xml/formhttpurl encoded
#Bean
public Encoder feignEncoder() {
ObjectFactory<HttpMessageConverters> objectFactory = () ->
new HttpMessageConverters(new FormHttpMessageConverter());
return new SpringEncoder(objectFactory);
}
Important FormHttpMessageConverter serialize only MultiValueMap subsclass
im my case it was due to outdated version of lombok, updating to 1.18.16 resolve it

Controller for JSON and HTML with Spring Boot

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;
}

Spring MVC + AngularJS 415 Unsupported Media Type

The question is very simple and I know it was answered in many other question but none worked for me. Using Spring MVC and AngularJS I am getting AngularJS 415 Unsupported Media Type !
I tried setting angular header to application/json
I tried with #Consumes annotation on Server Side
I tried with consumes ="application/json"
I tired with consumes ="application/application/json;charset=UTF-8'
I tried with consumes ={"application/json","application/xml"}
I also tried to setup produces property.
I tried explicitly setting the hear content type on the client to match exactly the one on the server but, NOTHING WORKED !
here are related questions that none actually helped !
1
2
3
Here is my Controller
/**
* Created by adelin.ghanayem#gmail.com
*/
#Controller
#RequestMapping(value = "/administration/places")
public class PlacesController {
private PlacesService service;
#Autowired
public PlacesController(PlacesService service) {
this.service = service;
}
#RequestMapping(method = RequestMethod.POST,consumes = {"application/json;charset=UTF-8"})
public String newPlace(#RequestBody Place places) {
String id = service.addNewPlace(places);
return "/administration/places/" + id;
}
#RequestMapping(value = "/{id}")
public Place getById(#PathVariable String id) {
return new Place();
}
}
And my AngularJS controller
function NewPlacesController(scope, http) {
scope.place = {};
scope.add = function () {
http.post(URLS.addNewPlace, scope.place,{'Content-Type': 'application/json'}).success(function (value) {
console.log("got it !");
}).error(function (value) {
console.log("CUR!");
});
}
}
NewPlacesController['$inject'] = ['$scope', '$http'];
Try it:
#RequestMapping(method = RequestMethod.POST,consumes = {"application/json;charset=UTF-8"}, produces={"application/json;charset=UTF-8"})
public String newPlace(#RequestBody Place places) {
String id = service.addNewPlace(places);
return "/administration/places/" + id;
}
And make sure you included Jackson Databind library.
Look at the content-type in your angular code. You may need to explicitly set it. Also look at your Spring MVC implementation. You can use the "REST Console" app in chrome to test your api to make sure it works the way you believe it should.
add in your pom :
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>

Spring Boot with AngularJS html5Mode

I start my web application with spring boot. It use a simple main class to start an embedded tomcat server:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I want to configure the server in the way that he can handle angularjs html5mode that will be activated with
$locationProvider.html5Mode(true);
Relevant postings from other users shows that you need to redirect to the root. the html5 mode remove the hashbag from the url. If you refresh the page the server doesnt find the page cause he do not handle the hash. see: AngularJS - Why when changing url address $routeProvider doesn't seem to work and I get a 404 error
Use this controller to forward the URI to index.html in order to preserve AngularJS routes. Source https://spring.io/blog/2015/05/13/modularizing-the-client-angular-js-and-spring-security-part-vii
#Controller
public class ForwardController {
#RequestMapping(value = "/**/{[path:[^\\.]*}")
public String redirect() {
// Forward to home page so that route is preserved.
return "forward:/";
}
}
In this solution ForwardController forwards only paths, which are not defined in any other Controller nor RestController. It means if you already have:
#RestController
public class OffersController {
#RequestMapping(value = "api/offers")
public Page<OfferDTO> getOffers(#RequestParam("page") int page) {
return offerService.findPaginated(page, 10);
}
}
both controllers are going to work properly - #RequestMapping(value = "api/offers") is checked before #RequestMapping(value = "/**/{[path:[^\\.]*}")
I had same problem. As far as I know, in html5 mode, angularjs don't resolve hash but entered url or url added through pushState.
The problem was that PathResourceResolver map directories but not files. Because it intended to serve requested files from directory but not to rewrite urls. For app it's mean, if you refresh your browser window or type url like http://example.com/mystate, it's query "/mystate" from the server. If spring don't know url, they return 404. One of the solutions is map every possible state to index.html like here (source, btw look at webjars - it's great!). But in my case I can safely map "/**" to index.html and therefore my solution is to override PathResourceResolver#getResource:
#Configuration
#EnableConfigurationProperties({ ResourceProperties.class })
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Autowired
private ResourceProperties resourceProperties = new ResourceProperties();
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
Integer cachePeriod = resourceProperties.getCachePeriod();
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCachePeriod(cachePeriod);
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/index.html")
.setCachePeriod(cachePeriod).resourceChain(true)
.addResolver(new PathResourceResolver() {
#Override
protected Resource getResource(String resourcePath,
Resource location) throws IOException {
return location.exists() && location.isReadable() ? location
: null;
}
});
}
}
I found a solution I can live with it.
#Controller
public class ViewController {
#RequestMapping("/")
public String index() {
return "index";
}
#RequestMapping("/app/**")
public String app() {
return "index";
}
}
The angularjs app has to be under the subdomain app. If you do not want that you could create a subdomain like app.subdomain.com that mapps to your subdomain app. With this construct you have no conflicts with webjars, statis content and so on.
A small adjustment to a previous code which works to me.
// Running with Spring Boot v1.3.0.RELEASE, Spring v4.2.3.RELEASE
#Configuration
#EnableConfigurationProperties({ ResourceProperties.class })
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Autowired
private ResourceProperties resourceProperties = new ResourceProperties();
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
Integer cachePeriod = resourceProperties.getCachePeriod();
final String[] staticLocations = resourceProperties.getStaticLocations();
final String[] indexLocations = new String[staticLocations.length];
for (int i = 0; i < staticLocations.length; i++) {
indexLocations[i] = staticLocations[i] + "index.html";
}
registry.addResourceHandler(
"/**/*.css",
"/**/*.html",
"/**/*.js",
"/**/*.json",
"/**/*.bmp",
"/**/*.jpeg",
"/**/*.jpg",
"/**/*.png",
"/**/*.ttf",
"/**/*.eot",
"/**/*.svg",
"/**/*.woff",
"/**/*.woff2"
)
.addResourceLocations(staticLocations)
.setCachePeriod(cachePeriod);
registry.addResourceHandler("/**")
.addResourceLocations(indexLocations)
.setCachePeriod(cachePeriod)
.resourceChain(true)
.addResolver(new PathResourceResolver() {
#Override
protected Resource getResource(String resourcePath,
Resource location) throws IOException {
return location.exists() && location.isReadable() ? location
: null;
}
});
}
}
You can forward all not found resources to your main page by providing custom ErrorViewResolver.
All you need to do is to add this to your #Configuration class:
#Bean
ErrorViewResolver supportPathBasedLocationStrategyWithoutHashes() {
return new ErrorViewResolver() {
#Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
return status == HttpStatus.NOT_FOUND
? new ModelAndView("index.html", Collections.<String, Object>emptyMap(), HttpStatus.OK)
: null;
}
};
}
I finally get my Angular 5 application working with spring boot with or without spring-boot-starter-tomcat as provided (embedded) or not!
/**
* Needed for html5mode (PathLocationStrategy in Angular). Every path except api/* and resources (css, html, js, woff, etc..)
* should be redirect to index.html and then should angular managed routes (which could be correct or non existing).
*/
#RestController
#RequestMapping
public class ForwardController {
#GetMapping(value = "/**/{[path:[^\\.]*}")
public ModelAndView forward() {
return new ModelAndView("/index.html");
}
}
I just encountered the similar issue where I wanted to configure Resources and at the same time I wanted to use AngularJS Html5 mode enabled.
In my case my static files were served from /public route so I used the following request mapping on my index action and it all works fine.
#RequestMapping(value = {"", "/", "/{[path:(?!public).*}/**"}, method = GET)
public String indexAction() {
return "index";
}
I had the same problem while using angular Html5Mode.
The solution that worked for me was to configure error page for 404 in web.xml assigning the path to my Index view in my case "/".
<error-page>
<error-code>404</error-code>
<location>/</location>
</error-page>
Similarly, you can try configuring error page in spring boot. for reference, you can check this link.
Spring boot and custom 404 error page
1- first you create new Controller then copy and paste simple below code
#Controller
public class viewController {
#RequestMapping(value = "/**/{[path:[^\\.]*}")
public String redirect() {
// Forward to home page so that route is preserved.
return "forward:/";
}
}
3- remove 2 below item from angular app
$locationProvider.hashPrefix('!');
$urlRouterProvider.otherwise("/");
2- in angular application you must add $locationProvider.html5Mode(true); to app route
3- Don't forget to place the base tag before any http request in your index.html file
<head>
<base href="/"> /* Or whatever your base path is */
//call every http request for style and other
...
</head>
it's work fine for me