自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

為啥前端給到的參數(shù)又不對(duì),該如何傳遞呢?

開(kāi)發(fā) 前端
最常見(jiàn)的一種傳輸參數(shù)的方式,URL 查詢參數(shù)是指附加在 URL 后面的以鍵值對(duì)形式傳遞的參數(shù),通常用于 GET 請(qǐng)求中向服務(wù)器傳遞簡(jiǎn)單的數(shù)據(jù)。 ?

在Spring Boot開(kāi)發(fā)過(guò)程中,前端與后端之間的傳參是一個(gè)核心且常見(jiàn)的問(wèn)題。本文將詳細(xì)探討前端如何向后端傳遞參數(shù)、后端如何接收參數(shù)、接收參數(shù)的原理,以及在實(shí)際開(kāi)發(fā)中如何進(jìn)行合理的配置與設(shè)置,確保參數(shù)能夠正確、安全地傳輸和處理。

六種常見(jiàn)的方式

URL 查詢參數(shù)

最常見(jiàn)的一種傳輸參數(shù)的方式,URL 查詢參數(shù)是指附加在 URL 后面的以鍵值對(duì)形式傳遞的參數(shù),通常用于 GET 請(qǐng)求中向服務(wù)器傳遞簡(jiǎn)單的數(shù)據(jù)。  

// 前端
axios.get('/api/users', {
  params: {
    name: 'John',
    age: 30
  }
});
// 后端
@GetMapping("/users")
public ResponseEntity<?> getUser(@RequestParam("name") String name,
                                 @RequestParam("age") Integer age) {
    // 處理業(yè)務(wù)邏輯
    return ResponseEntity.ok("User: " + name + ", age: " + age);
}

注意:我們可以去省略這個(gè)@RequestParam 注解,使用這個(gè)注解的好處就是可以設(shè)置參數(shù)的默認(rèn)值defaultValue。

路徑參數(shù)

直接將參數(shù)嵌入到 URL 路徑當(dāng)中的一種傳遞方式,對(duì)于后端而言需要指定特殊的標(biāo)識(shí)符和注解才能使用。

// 前端
axios.get('/api/users/123');
// 后端
@GetMapping("/users/{id}")
public ResponseEntity<?> getUserById(@PathVariable("id") Long id) {
    // 根據(jù) id 查詢用戶
    return ResponseEntity.ok("User ID: " + id);
}

請(qǐng)求體參數(shù)

主要用于 POST、PUT 等請(qǐng)求,常傳遞 JSON 數(shù)據(jù)或表單數(shù)據(jù)。  

// 前端
axios.post('/api/users', {
  name: 'John',
  age: 30
});
// 后端
@PostMapping("/users")
public ResponseEntity<?> createUser(@RequestBody User user) {
    // user 對(duì)象由 JSON 數(shù)據(jù)自動(dòng)反序列化而來(lái)
    return ResponseEntity.ok("User created: " + user.getName());
}

注意:請(qǐng)求頭需要設(shè)置  Content-Type: application/json,主要用于解析 JSON 數(shù)據(jù) 。

表單數(shù)據(jù)參數(shù)

 表單數(shù)據(jù)指的是通過(guò) HTML 表單或 application/x-www-form-urlencoded 方式提交的參數(shù),主要用于 POST、PUT 請(qǐng)求。后端通常使用 @RequestParam 或 @ModelAttribute 來(lái)解析。

// 前端
let formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('username', 'John');


axios.post('/api/upload', formData, {
  headers: {
    'Content-Type': 'multipart/form-data'
  }
});
// 后端
// 普通表單接收方式
@PostMapping("/users/form")
public ResponseEntity<?> createUserForm(@ModelAttribute User user) {
    return ResponseEntity.ok("User created via form: " + user.getName());
}
// 涉及文件上傳表單接收方式
@PostMapping("/upload")
public ResponseEntity<?> handleFileUpload(@RequestPart("file") MultipartFile file,
                                          @RequestParam("username") String username) {
    // 處理文件上傳和其他參數(shù)
    return ResponseEntity.ok("File uploaded by: " + username);
}

post 方式只有一個(gè)屬性

@PostMapping("/string")
public ResponseEntity<?> receiveString(@RequestParam("text") String text) {
    return ResponseEntity.ok("Received: " + text);
}


axios.post('/string', 'text=Hello%20World', {
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  }
})


@PostMapping("/string")
public ResponseEntity<?> receiveString(@RequestBody String text) {
    return ResponseEntity.ok("Received: " + text);
}


axios.post('/string', 'Hello World', {
  headers: {
    'Content-Type': 'text/plain'
  }
})

注意:@RequestParam 方式(不可也可以,但是加上更清晰可讀)下可直接放在 Url 后面,@RequestBody 方式下使用 json 格式傳遞 。

圖片圖片

post 請(qǐng)求傳遞數(shù)組

@PostMapping("/list")
public ResponseEntity<?> receiveList(@RequestBody List<String> list) {
    return ResponseEntity.ok("Received: " + list);
}


axios.post('/list', ["apple", "banana", "cherry"], {
    headers: { 'Content-Type': 'application/json' }
})


@PostMapping("/list")
public ResponseEntity<?> receiveList(@RequestParam List<String> list) {
    return ResponseEntity.ok("Received: " + list);
}
axios.post('/list', null, { params: { list: ["apple", "banana", "cherry"] } })
.then(response => console.log(response.data))
.catch(error => console.error(error));
http://localhost:8080/list?list=apple&list=banana&list=cherry

圖片圖片

圖片圖片

原理分析

圖片圖片

RequestParamMethodArgumentResolver 類

在 Spring MVC 中,@RequestParam 參數(shù)的解析由 RequestParamMethodArgumentResolver 負(fù)責(zé)。它是 HandlerMethodArgumentResolver 的實(shí)現(xiàn)類之一,專門用于解析 @RequestParam 注解的參數(shù)。  

// 判斷是否由該類進(jìn)行解析
public boolean supportsParameter(MethodParameter parameter) {
    if (parameter.hasParameterAnnotation(RequestParam.class)) {
        if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
            RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
            return (requestParam != null && StringUtils.hasText(requestParam.name()));
        }
        else {
            return true;
        }
    }
    else {
        if (parameter.hasParameterAnnotation(RequestPart.class)) {
            return false;
        }
        parameter = parameter.nestedIfOptional();
        if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
            return true;
        }
        else if (this.useDefaultResolution) {
            return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
        }
        else {
            return false;
        }
    }
}


// 具體解析邏輯
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {


    NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
    MethodParameter nestedParameter = parameter.nestedIfOptional();


    Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
    if (resolvedName == null) {
        throw new IllegalArgumentException(
                "Specified name must not resolve to null: [" + namedValueInfo.name + "]");
    }


    Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
    if (arg == null) {
        if (namedValueInfo.defaultValue != null) {
            arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
        }
        else if (namedValueInfo.required && !nestedParameter.isOptional()) {
            handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
        }
        arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
    }
    else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
        arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
    }


    if (binderFactory != null) {
        WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
        try {
            arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
        }
        catch (ConversionNotSupportedException ex) {
            throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
                    namedValueInfo.name, parameter, ex.getCause());
        }
        catch (TypeMismatchException ex) {
            throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
                    namedValueInfo.name, parameter, ex.getCause());
        }
        // Check for null value after conversion of incoming argument value
        if (arg == null && namedValueInfo.defaultValue == null &&
                namedValueInfo.required && !nestedParameter.isOptional()) {
            handleMissingValueAfterConversion(namedValueInfo.name, nestedParameter, webRequest);
        }
    }


    handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);


    return arg;
}


protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
    HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);


    if (servletRequest != null) {
        Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
        if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
            return mpArg;
        }
    }


    Object arg = null;
    MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
    if (multipartRequest != null) {
        List<MultipartFile> files = multipartRequest.getFiles(name);
        if (!files.isEmpty()) {
            arg = (files.size() == 1 ? files.get(0) : files);
        }
    }
    if (arg == null) {
        String[] paramValues = request.getParameterValues(name);
        if (paramValues != null) {
            arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
        }
    }
    return arg;
}

解析條件:首先調(diào)用supportsParameter 方法判斷是否需要該類進(jìn)行解析,判斷邏輯參數(shù)上存在@RequestParam 注解或者未標(biāo)@RequestParam注解,但是 useDefaultResolutinotallow=true 也會(huì)嘗試解析。

解析邏輯:調(diào)用該類父類AbstractNamedValueMethodArgumentResolver 的resolveArgument 方法解析參數(shù)。

關(guān)鍵點(diǎn):該類的resolveName 方法是從request.getParameter()獲取參數(shù)值。

PathVariableMethodArgumentResolver 類

@PathVariable 注解的解析由 PathVariableMethodArgumentResolver 負(fù)責(zé),它的解析邏輯與 @RequestParam 類似,但它解析的是 路徑參數(shù),而非查詢參數(shù)。

public boolean supportsParameter(MethodParameter parameter) {
    if (!parameter.hasParameterAnnotation(PathVariable.class)) {
        return false;
    }
    if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
        PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
        return (pathVariable != null && StringUtils.hasText(pathVariable.value()));
    }
    return true;
}

解析條件:首先調(diào)用supportsParameter 方法判斷是否需要該類進(jìn)行解析,判斷邏輯參數(shù)上存在@PathVariable 注解。

解析邏輯:與前面@RequestParam的一致。

關(guān)鍵點(diǎn):該類的resolveName 方法是從 HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE  獲取參數(shù)值。

RequestResponseBodyMethodProcessor 類

該類是用來(lái) 處理 @RequestBody 和 @ResponseBody注解的,它主要用于解析請(qǐng)求體(@RequestBody)和返回值(@ResponseBody),并完成 JSON/XML 的序列化和反序列化。    

public boolean supportsParameter(MethodParameter parameter) {
    return parameter.hasParameterAnnotation(RequestBody.class);
}


@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {


    parameter = parameter.nestedIfOptional();
    Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
    String name = Conventions.getVariableNameForParameter(parameter);


    if (binderFactory != null) {
        WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
        if (arg != null) {
            validateIfApplicable(binder, parameter);
            if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
            }
        }
        if (mavContainer != null) {
            mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
        }
    }


    return adaptArgumentIfNecessary(arg, parameter);
}

解析條件:首先調(diào)用supportsParameter 方法判斷是否需要該類進(jìn)行解析,判斷邏輯是存在@RequestBody 注解。

解析邏輯:直接自身的resolveArgument 方法,通過(guò)readWithMessageConverters 方法進(jìn)行請(qǐng)求體轉(zhuǎn)換,獲取參數(shù)名稱,進(jìn)行數(shù)據(jù)綁定和校驗(yàn),適配參數(shù)。

拓展分析

如果內(nèi)置的參數(shù)綁定方式無(wú)法滿足特定的要求,我們可以通過(guò)自定義 HandlerMethodArgumentResolver  來(lái)實(shí)現(xiàn)獨(dú)特的參數(shù)方式,這樣可以做到更加的安全可靠,需要將自定義解析器加入鏈條當(dāng)中。

public class CustomArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // 根據(jù)條件判斷是否支持該參數(shù)解析
        return parameter.hasParameterAnnotation(MyCustomAnnotation.class);
    }


    @Override
    public Object resolveArgument(MethodParameter parameter,
                                  ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest,
                                  WebDataBinderFactory binderFactory) throws Exception {
        // 自定義解析邏輯
        return ...;
    }
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new CustomArgumentResolver());
    }
}

具體的實(shí)現(xiàn)邏輯可以根據(jù)需求去添加,該方式適合于復(fù)雜的數(shù)據(jù)轉(zhuǎn)換和自定義注解綁定邏輯,相對(duì)于統(tǒng)一的傳參方式更加隱秘安全。

責(zé)任編輯:武曉燕 來(lái)源: java從零到壹
相關(guān)推薦

2017-12-05 08:53:20

Golang參數(shù)傳遞

2025-02-12 10:51:51

2024-02-26 08:52:20

Python傳遞函數(shù)參數(shù)參數(shù)傳遞類型

2010-06-04 10:46:18

同事上司

2023-01-05 08:12:11

分層應(yīng)用代碼

2020-05-22 18:16:57

數(shù)據(jù)泄露網(wǎng)絡(luò)安全互聯(lián)網(wǎng)

2023-07-29 22:11:58

Spring容器Component

2023-04-10 15:14:03

2015-11-09 10:29:05

設(shè)計(jì)師前端

2020-12-23 07:56:40

前端UICSS

2009-06-09 21:54:26

傳遞參數(shù)JavaScript

2021-02-24 14:01:13

微服務(wù)開(kāi)發(fā)框架

2021-05-20 10:11:51

固態(tài)硬盤碎片系統(tǒng)

2025-03-06 08:45:03

2015-05-15 17:29:13

.Netxp系統(tǒng)如何解決

2024-09-19 20:59:49

2018-07-27 17:28:37

人工智能機(jī)器學(xué)習(xí)AI

2023-11-03 08:18:59

PostgresMySQL

2020-01-08 15:31:41

MybatisHibernateJPA

2009-07-30 13:07:49

ASP.NET中的三層
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)