Spring MVC: Website Design and RESTful Services

You can use this article to understand how to use Spring MVC to create websites or RESTful services. And also get an overview of frequently asked questions covering the most common tasks of Spring MVC.


Note: An article of ~ 7,500 words is probably not worth reading on a mobile device. Bookmark it and come back later.


Content



Introduction


What is Spring MVC?


Spring MVC β€” - Spring. - RESTful (, JSON/XML) Spring, , REST Spring Boot .


, ?


, : .


( , Spring Spring Boot, , Spring Framework?)


HttpServlets 101


- Java Spring (MVC/Boot) , :


  1. HTML β†’ - HTML-, .
  2. JSON/XML β†’ - RESTful, JSON XML. Javascript - , .
  3. , , .

- ? Java?


- Java HttpServlets. HTML, JSON XML. , 1 - Java (Spring MVC, Wicket, Struts) HttpServlets.


( : HttpServlets, .)


HTML- HttpServlets


HttpServlet, HTML-.


package com.marcobehler.springmvcarticle;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class MyServletV1 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        if (req.getRequestURI().equals("/")) {
            resp.setContentType("text/html");
            resp.getWriter().print("<html><head></head><body><h1>Welcome!</h1><p>This is a very cool page!</p></body></html>");
        }
        else {
            throw new IllegalStateException("Help, I don't know what to do with this url");
        }
    }
}

.


public class MyServletV1 extends HttpServlet {

Java HttpServlet.


@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {

() GET, doGet () . POST doPost (). HTTP .


if (req.getRequestURI().equals("/")) {

, URL- , . Β«/Β», www.marcobehler.com, www.marcobehler.com/hello.


resp.setContentType("text/html");

ServletResponse, , . HTML.


resp.getWriter().print("<html><head></head><body><h1>Welcome!</h1><p>This is a very cool page!</p></body></html>");

: - β€” HTML! HTML- ServletResponse. response writer.


, Tomcat Jetty. , , , :


package com.marcobehler.springmvcarticle;

import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Wrapper;
import org.apache.catalina.startup.Tomcat;

public class TomcatApplicationLauncher {

    public static void main(String[] args) throws LifecycleException {
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);
        tomcat.getConnector();

        Context ctx = tomcat.addContext("", null);
        Wrapper servlet = Tomcat.addServlet(ctx, "myServlet", new MyServletV2());
        servlet.setLoadOnStartup(1);
        servlet.addMapping("/*");

        tomcat.start();
    }
}

.


Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);
tomcat.getConnector();

Tomcat, 8080.


        Context ctx = tomcat.addContext("", null);
        Wrapper servlet = Tomcat.addServlet(ctx, "myServlet", new MyServletV2());

Tomcat. , Tomcat .


        servlet.addMapping("/*");

Tomcat, , . /* , (/users, /register, /checkout).


        tomcat.start();

. main(), 8080 - (http://localhost:8080 /), HTML.


, , doGet () doPost (), - . .


JSON HttpServlets


, ( ) HTML- REST API . React AngularJS URL- :


/api/users/{userId}

JSON userId. MyServlet , , - ?


package com.marcobehler.springmvcarticle;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class MyServletV2 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        if (req.getRequestURI().equals("/")) {
            resp.setContentType("text/html");
            resp.getWriter().print("<html><head></head><body><h1>Welcome!</h1><p>This is a very cool page!</p></body></html>");
        } else if (req.getRequestURI().startsWith("/api/users/")) {

            Integer prettyFragileUserId = Integer.valueOf(req.getRequestURI().lastIndexOf("/") + 1);

            resp.setContentType("application/json");

            // User user = dao.findUser(prettyFragileUserId)
            // actually: jsonLibrary.toString(user)
            resp.getWriter().print("{\n" +
                    "  \"id\":" + prettyFragileUserId + ",\n" +
                    "  \"age\": 55,\n" +
                    "  \"name\" : \"John Doe\"\n" +
                    "}");
        } else {
            throw new IllegalStateException("Help, I don't know what to do with this url");
        }
    }
}

.


        } else if (req.getRequestURI().startsWith("/api/users/")) {

if doGet /api/users/.


  Integer prettyFragileUserId = Integer.valueOf(req.getRequestURI().lastIndexOf("/") + 1);

URL. URL β€” userID, , 5 /api/users/5. , int, !


resp.setContentType("application/json");

JSON .


// User user = dao.findUser(prettyFragileUserId)
// actually: jsonLibrary.toString(user)
resp.getWriter().print("{\n" +
        "  \"id\":" + prettyFragileUserId + ",\n" +
        "  \"age\": 55,\n" +
        "  \"name\" : \"John Doe\"\n" +
        "}");

, JSON β€” , HTTPServletResponse. , JSON Java- , .


Β« Β»


, :


  1. HTTP- , URI , .. : .
  2. , . : , . , .
  3. JSON HTML .

, ? , ? URI , JSON, ?


Spring MVC.


DispatcherServlet


Spring MVC , Model-View-Controller. .


, Spring MVC β€” , -?


DispatcherServlet.


( , , , )


Spring MVC DispatcherServlet?


, - Java , Spring MVC , HTTP- ( DispatcherServlet -).


HTTP-, ? Β« Β», HTML .


DispatcherServlet :


  1. URI HTTP . : POST /register?name=john&age33.
  2. (/ ) Java REST ,, .
  3. , .. , , , , .
  4. HTML/JSON/XML.

, , DispatcherServlet .



. ModelAndView ? DispatcherServlet ?


Let's-write-HTML? .


β€” HTML


, HTML , Spring MVC ( Spring Boot), . .


Spring


(POST/register?name=john&age33) .


package com.marcobehler.springmvcarticle;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class RegistrationController {

    @PostMapping("/register")
    public String registerUser(@RequestParam(required = false) Integer age, @RequestParam String name, Model model) {
        User user = new User(name, age);

        // TODO save user to database
        // userDao.save(user);

        // TODO send out registration email
        // mailService.sendRegistrationEmail(user);

        model.addAttribute("user", user);
        return "registration-success";
    }
}

.


@Controller
public class RegistrationController {

Spring Controller, .


@PostMapping("/register")

DispatcherServlet, , POST /register, (, ?Username=), .


    public String registerUser(@RequestParam(required = false) Integer age, @RequestParam String name, Model model) {

, .
, , URL (?age=10&name=Joe), POST. , name ( age )


age, , Integer ( , Integer)


, , Spring MVC model . , , HTML-, .


User user = new User(name, age);

// TODO save user to database
// userDao.save(user);

// TODO send out registration email
// mailService.sendRegistrationEmail(user);

, . , , . -.


model.addAttribute("user", user);

Β«userΒ». , HTML- , , Β«${user.name}Β». .


return "registration-success";

registration-success. , , .. HTML, , Spring .


Views ()


, (, , ) Spring MVC , .. , , registration-success.html.


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<p th:text="'Hello ' + ${user.name} + '!'"></p>

</body>
</html>

HTML-, . , .


<p th:text="'Hello ' + ${user.name} + '!'"></p>

, th:text=? Spring? - ?


: Spring MVC HTML. HTML- , , .


Thymeleaf, Spring MVC.


Spring MVC


, Spring MVC, : Thymeleaf, Velocity, Freemarker, Mustache JSP ( ).


, , , , HTML- β€” , .


, , , . , ?


ViewResolver?


, Spring HTML-, .


, , ViewResolver. , , Spring ViewResolvers , . ViewResolvers, .


, Thymeleaf. , ThymeleafViewResolver.


package com.marcobehler.springmvcarticle;

import org.springframework.context.annotation.Bean;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;

public class ThymeleafConfig {

    @Bean
    public ThymeleafViewResolver viewResolver() {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();

        SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
        templateResolver.setPrefix("classpath:/templates");
        templateResolver.setSuffix(".html");
        // some other lines neglected...

        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver);
        // some other lines neglected...

        viewResolver.setTemplateEngine(templateEngine);
        return viewResolver;
    }
}

.


    @Bean
    public ThymeleafViewResolver viewResolver() {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();

, ThymeleafViewResolver Spring ViewResolver. (: registration-success), ViewResolvers .


        SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();

ThymeleafViewResolver Thymeleaf . SpringResourceTemplateResolver. .


SpringResourceTemplateResolver Thymeleaf


templateResolver.setPrefix("classpath:/templates");
templateResolver.setSuffix(".html");

, ( Spring Resources): Β« classpath, /templatesΒ». .html. :


, String, registration-success, ThymeleafViewResolver : classpath:/templates/registration-success.html.


: Spring Boot


: , ViewResolver, Spring Boot. . Spring Boot , , spring-boot-starter-thymeleaf.


ViewResolver , src/main/resources/template.


, Spring Boot Spring MVC . .


: Model-View-Controller


Controller & ViewResolver, Spring Model-View-Controller.


  • (Controller, @PostMapping, @RequestParam) , .
  • ( ), . , .
  • β€” HTML. , (). HTTP-. HTTP- .

.


, , Spring , HTTP, - .



, Spring MVC HTTP.


  • requestURI, .
  • , , .

, HTTP-.


@GetMapping @RequestMappping


@GetMapping . *@RequestMapping*. :


@GetMapping("/books")
public void book() {
        //
}

/* these two mappings are identical */

@RequestMapping(value = "/books", method = RequestMethod.GET)
public void book2() {

}

@GetMapping, @[Post|Put|Delete|Patch]Mapping @RequestMapping(method=XXX). (Spring 4.3+) () URL, , @RequestMapping , Spring.


@RequestParam


HTTP-, URL (?Key=value) , @RequestParam.


, (, HTTP String int), .


@PostMapping("/users")   /* First Param is optional */
public User createUser(@RequestParam(required = false) Integer age, @RequestParam String name) {
   // does not matter
}

, 400 Bad Request , Spring Boot, , :


{"timestamp":"2020-04-26T08:34:34.441+0000","status":400,"error":"Bad Request","message":"Required Integer parameter 'age' is not present","path":"/users"}

, Spring @RequestParams - . " ".


, getter/setter.


@PostMapping("/users")   /* Spring   ,     getters and setters */
public User createUser(UserDto userDto) {
    //
}

@PathVariable


, URI , @PathVariable. , userId=123, URL: GET / users/123


  1. , {} .

, PathVariables .


@GetMapping("/users/{userId}")
   public User getUser(@PathVariable(required = false) String userId) {
       // ...
       return user;
   }

PathVariables, , Java- ( , getter/setter).


@GetMapping("/users/{userId}")
public User getUser(UserDto userDto) {
    // ...
    return user;
}

:


, HTML- Spring MVC :


  1. , "" . Spring , ( , ) .
  2. , . .
  3. , HTML , .
  4. , , Spring , HTML- .
  5. , , , Spring Boot , .

.


REST β€” XML/JSON


RESTFul , -. , -, () JSON XML. , , JSON, , JSON.


, JSON HTTP-.


POST http://localhost:8080/users

###
{"email": "angela@merkel.de"}

Java ( Spring MVC) JSON . , , . Java, Spring JSON.


public class UserDto {
    private String email;
    //...
}

, , HTML . RESTful , HTML , JSON.


HTTP β†’ Java Java β†’ HTTP .


, , Spring MVC REST .


REST


, XML/JSON, @RestController Controller. ( @RestController Controller, . FAQ ).


REST- , , :


package com.marcobehler.springmvcarticle;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collections;
import java.util.List;

@RestController
public class BankController {

    @GetMapping("/transactions/{userId}")
    public List<Transaction> transactions(String userId) {

        // find transaction by user
        // List<Transaction> = dao.findByUserId(userId);

        List<Transaction> transactions = Collections.emptyList();
        return transactions;
    }
}

.


@RestController
public class BankController {

BankController @RestController, Spring, HTML- ModelAndView. XML/JSON ( - ) HTTP.


public List<Transaction> transactions(String userId) {

String (). List, Spring JSON XML. , , Java Transaction (- - ):

[
  {
    "occurred": "28.04.2020 03:18",
    "description": "McDonalds - Binging",
    "id": 1,
    "amount": 10
  },
  {
    "occurred": "28.04.2020 06:18",
    "description": "Burger King - Never enough",
    "id": 2,
    "amount": 15
  }
]

Spring MVC , JSON? XML? YAML? REST , ?


Spring .


β€” Accept Header


, , , REST .


? Accept HTTP-.


GET http://localhost:8080/transactions/{userid}
Accept: application/json

Spring MVC Accept : JSON (application/json), List JSON. ( . , Accept .)

, HTTP, .


. .


β€” Content-Type Header ( )


RESTful API , JSON XML. , REST :


POST http://localhost:8080/users

###
{"email": "angela@merkel.de"}

Spring , JSON, XML YAML? , , , Content-Type.


POST ...

Content-Type: application/json; charset=UTF-8

###
...

REST ?


package com.marcobehler.springmvcarticle;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class BookingController {

    @PostMapping("/transactions")
    public Transaction transaction(@RequestBody TransactionDto dto) {
        // do something with the dto..create the booking..convert it to a transaction
        Transaction transaction = null;
        return transaction;
    }
}

.


    public Transaction transaction(@RequestBody TransactionDto dto) {

@RequestParam @Pathvariable, , @RequestBody.


@RequestBody Content-Type Spring , HTTP- Content-Type, : JSON .


    // do something with the dto..create the booking..convert it to a transaction
    Transaction transaction = null;
    return transaction;
}

JSON, TransactionDTO, , Transaction, . Spring MVC.


Spring


: Spring Accept Content-Type, , Java JSON. XML. .


( / / .)


, Spring MVC , HttpMessageConverters.


HttpMessageConverter?


HttpMessageConverter β€” ( , , ).


  1. canRead (MediaType) β†’ (JSON | XML | YAML | . .)? MediaType Content-Type.
  2. canWrite (MediaType) β†’ (JSON | XML | YAML | . .)? MediaType, , Accept.
  3. read(Object, InputStream, MediaType) β†’ Java- (JSON | XML | YAML | . .) InputStream
  4. write(Object, OutputStream, MediaType) β†’ Java- OutputStream (JSON | XML | YAML | . .)

, MessageConverter , MediaTypes (, application/json), / .


HttpMessageConverters?


, . Spring MVC , HTTPMessageConverters β€” .


, . , Spring AllEncompassingFormHttpMessageConverter ( ).


static {
        ClassLoader classLoader = AllEncompassingFormHttpMessageConverter.class.getClassLoader();
        jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
        jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
                                        ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
        jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
        jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
        gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
        jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
}

.


jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);

Spring MVC javax.xml.bind.Binder , , , JAXB.


jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);

Spring MVC ..jackson..ObjectMapper ..jackson..JsonGenerator , , , Jackson JSON.


jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);

Spring MVC ..jackson..XmlMapper , , , XML Jackson s XML.


. Spring HttpMessageConverter , «».


if (jaxb2Present && !jackson2XmlPresent) {
        addPartConverter(new Jaxb2RootElementHttpMessageConverter());
}

if (jackson2Present) {
        addPartConverter(new MappingJackson2HttpMessageConverter());
}
else if (gsonPresent) {
        addPartConverter(new GsonHttpMessageConverter());
}

: Spring Boot


Spring Boot Spring MVC . Spring Boot Jackson .


JSON Spring Boot, HttpMessageConverts .


: REST


HTML JSON / XML , Model View.


Java, Spring MVC JSON / XML , HttpMessageConverters.


, :


  1. .
  2. Accept Content-Type .


- ?


GitHub:
https://github.com/marcobehler/spring-mvc-article


SpringMvcArticleApplication, -.


Spring MVC Spring Boot?


: , Spring Boot Spring MVC.


Spring Framework?.


Spring MVC?


Spring MVC, Spring Boot .


  1. : https://start.spring.io/.
  2. Spring Web .

/ RESTful- Spring MVC.


HTTP- Spring MVC?


Spring MVC , HTTP β€” .


, JSON, XML HTTP (Multipart) Fileuploads, Spring Java.


HTTP- Spring MVC?


Spring MVC HttpServletResponse β€” .


HTML, JSON, XML WebSocket. , Java .


REST


  1. HTML , @ResponseBody , XML / JSON.
  2. REST , @ResponseBody. @ResponseBody .

@Controller
@ResponseBody
public @interface RestController {

  1. REST XML / JSON HTML.

. XML JSON β€” , Spring MVC. / REST - , , YAML. , HttpMessageConverter ApplicationContext.


?


, , Thymeleaf Spring, . , Thymeleaf ( ), , .


404? .


, , JSON XML, @ResponseBody.


Spring 404 Not Found .


package com.marcobehler.springmvcarticle;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class _404WithMissingResponseBodyController {

    @GetMapping("/users/{id}")   /* This won't work and lead to a 404 */
    public User getUser_404(@PathVariable String id) {
        return new User("Everyone's name is John", id);
    }

    @GetMapping("/users2/{id}")
    @ResponseBody  /* This will work */
    public User getUser_200(@PathVariable String id) {
        return new User("Everyone's name is John", id);
    }
}

: @ResponseBody REST .


, ?


HTTP , .


/*   */

@PostMapping("/users")
public void method1() {

}

@GetMapping("/users")
publi void method(2) {

}

HTTP , .


/*    */

@PostMapping("/users")
public void method1() {

}

@PostMapping("/users")
publi void method(2) {

}

IllegalStateException, .


Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'howToPassAndRetrieveRequestParametersController' method
com.marcobehler.springmvcarticle.HowToPassAndRetrieveRequestParametersController#createUser(User)
to {POST /users3}: There is already 'howToPassAndRetrieveRequestParametersController' bean method

URL @RequestParams?


, Spring URL. :


, , , Β«+Β» , , marco+wantsnospam@marcobehler.com.


@GetMapping("/confirm")
public void confirm(@RequestParam String email, @RequestParam String token){
    // confirm user...
}

β€˜+’ URL - , @RequestParam?


Β«marco[space]wantnospam@marcobehler.comΒ», Spring + , RFC3986.


: , URL-, , : marco%2Bwantsnospam@marcobehler.com, Spring .


HttpSession ?


Spring MVC REST HttpSession , Spring ( , ).


@RestController
public class HttpSessionController {

    @GetMapping("/session")
    public String getSession(HttpSession httpSession) {
        System.out.println("httpSession = " + httpSession);
        return httpSession.getId();
    }
}

, HttpSession .


@Service
class SomeOtherService {

    @Autowired
    private HttpSession httpSession;

    public HttpSession getHttpSession() {
        return httpSession;
    }
}

HttpServletRequest?


Spring MVC REST HttpServletRequest , Spring (, )


@RestController
public class HttpServletRequestController {

    @Autowired
    private SomeRequestService someRequestService;

    @GetMapping("/request")
    public String getRequest(HttpServletRequest request) {
        System.out.println("request = " + request);
        return request.toString();
    }
}

, HttpServletRequest .


@Service
class SomeRequestService {

    @Autowired
    private HttpServletRequest httpServletRequest;

    public HttpServletRequest getRequest() {
        return httpServletRequest;
    }
}

HTTP ?


, , . @RequestHeader.


, .


package com.marcobehler.springmvcarticle;

import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;

import java.util.Map;

@Controller
public class HttpHeaderController {

    @GetMapping("/headers1")
    public void singleHeader(@RequestHeader("x-forwarded-for") String xForwardedFor) {
       // ...
    }

    @GetMapping("/headers2")
    public void headersAsMap(@RequestHeader Map<String,String> headers) {  // or MultiValueMap<String,String>
        // ...
    }

    @GetMapping("/headers3")
    public void headersAsObject(HttpHeaders headers) {
        // ...
    }
}


cookie @CookieValue . cookie HttpServletResponse.


package com.marcobehler.springmvcarticle;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

@Controller
public class CookieController {

    @GetMapping("/cookie")
    public void handle(@CookieValue("JSESSIONID") String cookie, HttpServletResponse response) {

        response.addCookie(new Cookie("SOME_COOKIE_NAME", "This is a crazy new cookie!"));
        //...
    }
}

IP- ?


. httpServletRequest.getRemoteAddr(), , , IP- -, , 99,99% Nginx Apache.


, X-Forwarded-For IP-. , , , CDN, CloudFront? X-Forwarded-For :


X-Forwarded-For: MaybeSomeSpoofedIp, realIp, cloudFrontIp

, , , , X-Forwarded-For. IP-. CloudFront , IP- CloudFront . !


, IP. , !


package com.marcobehler.springmvcarticle;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

@RestController
public class IpController {

    private static final String[] HEADERS_TO_TRY = {
            "X-Forwarded-For",
            "Proxy-Client-IP",
            "WL-Proxy-Client-IP",
            "HTTP_X_FORWARDED_FOR",
            "HTTP_X_FORWARDED",
            "HTTP_X_CLUSTER_CLIENT_IP",
            "HTTP_CLIENT_IP",
            "HTTP_FORWARDED_FOR",
            "HTTP_FORWARDED",
            "HTTP_VIA",
            "REMOTE_ADDR"};

    @GetMapping("/ip")
    public String getClientIpAddress(HttpServletRequest request) {
        for (String header : HEADERS_TO_TRY) {
            String ip = request.getHeader(header);
            if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
                return getRealClientIpAddress(ip);
            }
        }
        return request.getRemoteAddr();
    }

    /**
     * Goes through the supplied ip string (could be one or multiple). Traverses it through the right side...
     * and removes any known ip address ranges
     *
     * @param ipString
     * @return
     */
    public String getRealClientIpAddress(String ipString) {
        String[] manyPossibleIps = ipString.split(",");

        for (int i = manyPossibleIps.length - 1; i >= 0; i--) {
            String rightMostIp = manyPossibleIps[i].trim();
            if (isKnownAddress(rightMostIp)) {
                continue; // skip this ip as it is trusted
            } else {
                return rightMostIp;
            }
        }

        return ipString;
    }

    private boolean isKnownAddress(String rightMostIp) {
        // do your check here..for cloudfront you'd need to download their ip address ranges
        // from e.g. http://d7uri8nf7uskq.cloudfront.net/tools/list-cloudfront-ips
        // and compare the current ip against them
        return false;
    }
}

Spring MVC?


, HTML-, :


<form method="POST" enctype="multipart/form-data" action="/upload">
    File to upload:<input type="file" name="file" />
    <input type="submit" value="Upload" />
</form>

@PostMapping MultiPartFile, .


package com.marcobehler.springmvcarticle;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;

@Controller
public class FileUploadController {

    @PostMapping("/upload")
    public String handleFileUpload(@RequestParam MultipartFile file) throws IOException {
        // don't generate upload files like this in a real project.
        // give them random names and save their uploaded name as metadata in a database or similar
        final Path uploadDestination = Paths.get("C:\\uploads").resolve(file.getName());

        file.transferTo(uploadDestination);
        return "redirect:/";
    }
}

(xls, pdf, csv, jpg, zip) Spring ?


, HttpServletResponse byte[] .


, Spring- 'ResponseEntity '. , , .

  • β†’ FileSystemResource
  • β†’ ClassPathResource
  • Β«-Β» β†’ InputStreamResource
  • byte[] β†’ ByteArrayResource

, , HTTP- ( , ..).


package com.marcobehler.springmvcarticle;

import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.server.ResponseStatusException;

import java.io.IOException;

@Controller
public class FileDownloadController {

    @RequestMapping(value = "/download/{jpgName}", method = RequestMethod.GET)
    public ResponseEntity<Resource> downloadJpg(
            @PathVariable String jpgName) throws IOException {

        //  Resource downloadResource = new InputStreamResource(soimeinputStream)
        //  Resource downloadResource = new ByteArrayResource(someByteArray)
        //  Resource downloadResource = new FileSystemResource(someFile)
        final ClassPathResource downloadResource = new ClassPathResource(jpgName);

        if (!downloadResource.exists()) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
        }

        HttpHeaders headers = new HttpHeaders();

        // 1. set the correct content type
        headers.setContentType(MediaType.IMAGE_JPEG);

        // 2. set the correct content length, maybe stored in a db table
        headers.setContentLength(downloadResource.contentLength());

        // 3. if you want to force downloads, otherwise attachments might be displayed directly in the brwoser
        headers.setContentDispositionFormData("attachment", jpgName);

        return new ResponseEntity<>(downloadResource, headers, HttpStatus.OK);
    }
}

?


Spring MVC , , .


ControllerAdvice RestControllerAdvice @ResponseStatus @ExceptionHandler. :


  1. , REST .
  2. @ResponseStatus HTTP, .
  3. @ExceptionHandler , -.
  4. , REST .

package com.marcobehler.springmvcarticle;

import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;

@ControllerAdvice
public class GlobalControllerExceptionHandler {

    @ResponseStatus(HttpStatus.CONFLICT)  // 409
    @ExceptionHandler(SomeConflictException.class)
    public String handleConflict(SomeConflictException e, Model model) {
        // do something
        model.addAttribute("message", e.getMessage());
        return "new-template";
    }

    @ResponseStatus(HttpStatus.NOT_IMPLEMENTED)  // 409
    @ExceptionHandler(NotYetImplementedExceptoin.class)
    public void handleBandwithLimitExceeded(NotYetImplementedExceptoin e) {
        // do nothing;
    }
}

(400, 404 ..) ?


ResponseStatusException , , .


ResponseEntity, .


package com.marcobehler.springmvcarticle;

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.server.ResponseStatusException;

@Controller
public class HttpStatusCodeController {

    @GetMapping("/somePath")
    public void alwaysThrowsException() {
         //throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Meeepp, not found.");

        throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Well, that just wasn't right!");
    }
}

XYZ Spring MVC?


Spring MVC , , -.


, , , ViewHandlers, InitBinders, RootContexts, , .., Spring MVC. , .



. , :


  • Spring MVC β€” MVC-, - HTML JSON/XML -.
  • , Spring, Spring Boot.
  • -, , HTTP- / .

That's all for today. Thanks for reading.


Acknowledgments


Many thanks to Patricio "Pato" Moschcovich , who not only corrected this article, but also gave invaluable feedback!


All Articles