restful--spring

现在的系统一般不和别人交互肯定玩不起来,如果和别人交互,就要出现通信,传递信息,

第一次我及得就做了个移动的接口,当初这个疑惑啊,什么是接口,接口是什么,百度了也没人说个明白的,接口就是搭起A公司与B公司的信息传递的桥梁,由于A与B不能将所有信息共享,所以只能发布服务让客户端去调用,获取一些信息。当初看着老司机将框架搭好,上来就是一阵羡慕啊,等自己明白了才知道就是那么一会事。就是通信+数据

说了这么多直接来吧,AXIS2 、cxf这个我第一次用的,servlet(jetty)第二次用,http(封装)第三次用,dubbo(socket)第四次用,restful(增删查改)第五次个人实践

如果谁能总结更好的可以告诉我偶。

今天只说restful  这上面唯一缺点是不恩呢该直接贴图,还得导进来,麻烦,本来想去spring官网看下文档介绍,这都是介绍的啥啊,一点也不好,只能百度看一下别人的见解,自己实践了一下。

遇到的问题1  转自 http://blog.csdn.net/lijianqingfeng/article/details/36867037

用myeclipse写jsp,发现了如标题的错误,而且是报在第一行,第一行根本就没有“{”,何来的缺少“}”

心中暗骂估计又是myeclipse抽风了。

到网上一搜,发现是jsp文件中有onclick(${xxx})格式的东西,需要给${xxx}加上单引号,试了一下,果然不报错了。

遇到问题2

jquery-min.js /jquery-1.8.3.min.js引入后报错

转自  http://blog.csdn.net/pcxbest/article/details/22379667

遇到问题3

ava.lang.IllegalArgumentException: Invalid 'log4jConfigLocation' parameter: class path resource [log4j.properties] cannot be resolved to absolute file path because it does not reside in the file system

这个 问题感觉太坑爹了,由于我原来对前端接触少一办都是接触后台程序,这次读取log的配置是写在web.xml里的,配上了之后一直找不到,气的我直接<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
</context-param>放到这里了。如果原来是classpath:log4j.properties    谁知道什么原因可以评论下。

顺便说下web的运行顺序,原理都是我自己写程序读取xml文件,现在web程序是直接读取web.xml,所以我直接在里面引入其他xml就可以了。

启动web会先读取web.xml里面的listener和context-param,容器创建servletcontext,其他将共享上下文

加载顺序  servletcontext--context-param---

要飞了,调试程序中还有点瑕疵,直接上代码吧,大家都知道spring下的springmvc有个controller,restful这个思想就是直接应用到springmvc上了。

配置文件 自己去看吧也可以私聊我要

/**
 * 
 */
package org.xdemo.example.springrestful.controller;


import java.util.ArrayList;
import java.util.List;


import org.apache.log4j.Logger;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
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.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.xdemo.example.springrestful.entity.Person;
import org.xdemo.example.springrestful.entity.User;


/**
 * @作者 Goofy
 * @邮件 252878950@qq.com
 * @日期 2014-4-2下午1:28:07
 */
@Controller
@RequestMapping("/user")
public class UserController {


/** 日志实例 */
private static final Logger logger = Logger.getLogger(UserController.class);


@RequestMapping(value = "/hello")
public @ResponseBody
String hello() {
return "你好!hello";
}


@RequestMapping(value = "/say/{msg}", produces = "application/json;charset=UTF-8")
public @ResponseBody
String say(@PathVariable(value = "msg") String msg) {
return "{\"msg\":\"you say:'" + msg + "'\"}";
}


// @RequestMapping(value = "/person/{id:\\d+}", method = RequestMethod.GET)
// public @ResponseBody
// Person getPerson(@PathVariable("id") int id) {
// logger.info("获取人员信息id=" + id);
// Person person = new Person();
// person.setName("张三");
// person.setSex("男");
// person.setAge(30);
// person.setId(id);
// return person;
// }


@RequestMapping(value = "/person/{id:\\d+}", method = RequestMethod.DELETE)
public @ResponseBody
String getPerson(@PathVariable("id") int id) {
logger.info("获取人员信息id=" + id);
Person person = new Person();
person.setName("张三");
person.setSex("男");
person.setAge(30);
person.setId(id);
return "person";
}


web发布了服务,ajax可以这样请求

function deleteUser(id){
$.ajax({
type: 'delete',
url:'<%=basePath%>user/'+id,
dataType:'text', 
success:function(data){
if(data=="suc"){
alert("删除成功");
location.reload();
}
},
error:function(data){
}
});
}

有个项目用到了这个,竟然让他当服务端,对方只提供url,提交方式有post,get,delete等,分别对应数据的增删查改。说白了就是服务端不需要提供方法只提供uri.

public class HttpclientTest {
private static final String targetURL = "http://localhost:8100/SpringRestful/user/person/1";


public static void main(String[] args) {
URL url = null;
try {
url = new URL(targetURL);
} catch (MalformedURLException exception) {
exception.printStackTrace();
}
HttpURLConnection httpURLConnection = null;
try {
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
httpURLConnection.setRequestMethod("DELETE");
System.out.println(httpURLConnection.getResponseCode());
} catch (IOException exception) {
exception.printStackTrace();
} finally {
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
}
}


我本机模范调用,但是一定要根据实际情况来看,因为请求方式就那么几个,但是请求的头部,解析啊什么的比较复杂,得注意。

终于写完了,哎,什么都得实践啊,



来一波源码

1     // org.springframework.web.bind.annotation.RequestMapping
 2     @Target({ElementType.METHOD, ElementType.TYPE})
 3     @Retention(RetentionPolicy.RUNTIME)
 4     @Documented
 5     @Mapping
 6     public @interface RequestMapping {
 7 
 8         /**
 9          * url路径,如/myPath/*.do
10          */
11         String[] value() default {};
12 
13         /**
14          * HTTP request methods 如:GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.
15          */
16         RequestMethod[] method() default {};
17 
18         /**
19          * requeset parameter 有3种匹配方式,是否包含某个参数,参数值相等,参数值不等于某个值,如myParam!=myValue
20          */
21         String[] params() default {};
22 
23         /**
24          * request的header
25          */
26         String[] headers() default {};
27 
28         /**
29          * request的MediaType
30          */
31         String[] consumes() default {};
32 
33         /**
34          * response的MediaType
35          */
36         String[] produces() default {};
37 
38     }
39 }

接着在这里补充吧以下内容转载自 http://blog.sina.com.cn/s/blog_72827fb10101pl9i.html-----主要是@requestMapping的参数问题

@RequestMapping 用法详解之地址映射

  (2013-08-11 16:06:58)
标签: 

it

 

前段时间项目中用到了RESTful模式来开发程序,但是当用POST、PUT模式提交数据时,发现服务器端接受不到提交的数据(服务器端参数绑定没有加任何注解),查看了提交方式为application/json, 而且服务器端通过request.getReader() 打出的数据里确实存在浏览器提交的数据。为了找出原因,便对参数绑定(@RequestParam、 @RequestBody、 @RequestHeader 、 @PathVariable)进行了研究,同时也看了一下HttpMessageConverter的相关内容,在此一并总结。


简介:

@RequestMapping

RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

RequestMapping注解有六个属性,下面我们把她分成三类进行说明。

1、 value, method;

value:     指定请求的实际地址,指定的地址可以是URI Template 模式(后面将会说明);

method:  指定请求的method类型, GET、POST、PUT、DELETE等;


2、 consumes,produces;

consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;

produces:    指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;


3、 params,headers;

params: 指定request中必须包含某些参数值是,才让该方法处理。

headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。


示例:

1、value  / method 示例

默认RequestMapping("....str...")即为value的值;

 

[java]  view plain copy
  1. @Controller  
  2. @RequestMapping("/appointments" 
  3. public class AppointmentsController  
  4.   
  5.     private final AppointmentBook appointmentBook;  
  6.       
  7.     @Autowired  
  8.     public AppointmentsController(AppointmentBook appointmentBook)  
  9.         this.appointmentBook appointmentBook;  
  10.      
  11.   
  12.     @RequestMapping(method RequestMethod.GET)  
  13.     public Map get()  
  14.         return appointmentBook.getAppointmentsForToday();  
  15.      
  16.   
  17.     @RequestMapping(value="/{day}"method RequestMethod.GET)  
  18.     public Map getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model)  
  19.         return appointmentBook.getAppointmentsForDay(day);  
  20.      
  21.   
  22.     @RequestMapping(value="/new"method RequestMethod.GET)  
  23.     public AppointmentForm getNewForm()  
  24.         return new AppointmentForm();  
  25.      
  26.   
  27.     @RequestMapping(method RequestMethod.POST)  
  28.     public String add(@Valid AppointmentForm appointment, BindingResult result)  
  29.         if (result.hasErrors())  
  30.             return "appointments/new" 
  31.          
  32.         appointmentBook.addAppointment(appointment);  
  33.         return "redirect:/appointments" 
  34.      
  35.  

 

 

 

 

 

value的uri值为以下三类:

A) 可以指定为普通的具体值;

B)  可以指定为含有某变量的一类值(URI Template Patterns with Path Variables);

C) 可以指定为含正则表达式的一类值( URI Template Patterns with Regular Expressions);


example B)

 

[java]  view plain copy
  1. @RequestMapping(value="/owners/{ownerId}"method=RequestMethod.GET)  
  2. public String findOwner(@PathVariable String ownerId, Model model)  
  3.   Owner owner ownerService.findOwner(ownerId);    
  4.   model.addAttribute("owner"owner);    
  5.   return "displayOwner"  
  6.  

example C)

 

 

 

 

 

 

[java]  view plain copy
  1. @RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\d\.\d\.\d}.{extension:\.[a-z]}" 
  2.   public void handle(@PathVariable String version, @PathVariable String extension)      
  3.     // ...  
  4.    
  5.  


 

 

 

 

 

2 consumes、produces 示例

cousumes的样例:

 

[java]  view plain copy
  1. @Controller  
  2. @RequestMapping(value "/pets"method RequestMethod.POST, consumes="application/json" 
  3. public void addPet(@RequestBody Pet pet, Model model)      
  4.     // implementation omitted  
  5.  
方法仅处理request Content-Type为“application/json”类型的请求。

 

 

 

 

 


produces的样例:

[java]  view plain copy
  1. @Controller  
  2. @RequestMapping(value "/pets/{petId}"method RequestMethod.GET, produces="application/json" 
  3. @ResponseBody  
  4. public Pet getPet(@PathVariable String petId, Model model)      
  5.     // implementation omitted  
  6.  

方法仅处理request请求中Accept头中包含了"application/json"的请求,同时暗示了返回的内容类型为application/json;

 


3 params、headers 示例

params的样例:

[java]  view plain copy
  1. @Controller  
  2. @RequestMapping("/owners/{ownerId}" 
  3. public class RelativePathUriTemplateController  
  4.   
  5.   @RequestMapping(value "/pets/{petId}"method RequestMethod.GET, params="myParam=myValue" 
  6.   public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model)      
  7.     // implementation omitted  
  8.    
  9.  

 仅处理请求中包含了名为“myParam”,值为“myValue”的请求;

 


headers的样例:

[java]  view plain copy
  1. @Controller  
  2. @RequestMapping("/owners/{ownerId}" 
  3. public class RelativePathUriTemplateController  
  4.   
  5. @RequestMapping(value "/pets"method RequestMethod.GET, headers="Referer=http://www.ifeng.com/" 
  6.   public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model)      
  7.     // implementation omitted  
  8.    
  9.  

 仅处理request的header中包含了指定“Refer”请求头和对应值为“http://www.ifeng.com/”的请求;

 



上面仅仅介绍了,RequestMapping指定的方法处理哪些请求,下面一篇将讲解怎样处理request提交的数据(数据绑定)和返回的数据。

@RequestParam @RequestBody @PathVariable 等参数绑定注解详解

(2013-08-11 16:09:23)
   

简介:

handler method 参数绑定常用的注解,我们根据他们处理的Request的不同内容部分分为四类:(主要讲解常用类型)

A、处理requet uri 部分(这里指uri template中variable,不含queryString部分)的注解:   @PathVariable;

B、处理request header部分的注解:   @RequestHeader, @CookieValue;

C、处理request body部分的注解:@RequestParam,  @RequestBody;

D、处理attribute类型是注解: @SessionAttributes, @ModelAttribute;

 

1、 @PathVariable 

当使用@RequestMapping URI template 样式映射时, 即 someUrl/{paramId}, 这时的paramId可通过 @Pathvariable注解绑定它传过来的值到方法的参数上。

示例代码:

 

[java]  view plain copy
  1. @Controller  
  2. @RequestMapping("/owners/{ownerId}" 
  3. public class RelativePathUriTemplateController  
  4.   
  5.   @RequestMapping("/pets/{petId}" 
  6.   public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model)      
  7.     // implementation omitted  
  8.    
  9.  
上面代码把URI template 中变量 ownerId的值和petId的值,绑定到方法的参数上。若方法参数名称和需要绑定的uri template中变量名称不一致,需要在@PathVariable("name")指定uri template中的名称。

 

2、 @RequestHeader、@CookieValue

@RequestHeader 注解,可以把Request请求header部分的值绑定到方法的参数上。

示例代码:

这是一个Request 的header部分:

 

[plain]  view plain copy
  1. Host                    localhost:8080  
  2. Accept                  text/html,application/xhtml+xml,application/xml;q=0.9  
  3. Accept-Language         fr,en-gb;q=0.7,en;q=0.3  
  4. Accept-Encoding         gzip,deflate  
  5. Accept-Charset          ISO-8859-1,utf-8;q=0.7,*;q=0.7  
  6. Keep-Alive              300  

[java]  view plain copy
  1. @RequestMapping("/displayHeaderInfo.do" 
  2. public void displayHeaderInfo(@RequestHeader("Accept-Encoding"String encoding,  
  3.                               @RequestHeader("Keep-Alive"long keepAlive)   
  4.   
  5.   //...  
  6.   
  7.  
上面的代码,把request header部分的 Accept-Encoding的值,绑定到参数encoding上了, Keep-Alive header的值绑定到参数keepAlive上。

 


@CookieValue 可以把Request header中关于cookie的值绑定到方法的参数上。

例如有如下Cookie值:

 

[java]  view plain copy
  1. JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84  
参数绑定的代码:

 

 

[java]  view plain copy
  1. @RequestMapping("/displayHeaderInfo.do" 
  2. public void displayHeaderInfo(@CookieValue("JSESSIONID"String cookie)   
  3.   
  4.   //...  
  5.   
  6.  
即把JSESSIONID的值绑定到参数cookie上。

 


3、@RequestParam, @RequestBody

@RequestParam 

A) 常用来处理简单类型的绑定,通过Request.getParameter() 获取的String可直接转换为简单类型的情况( String--> 简单类型的转换操作由ConversionService配置的转换器来完成);因为使用request.getParameter()方式获取参数,所以可以处理get 方式中queryString的值,也可以处理post方式中 body data的值;

B)用来处理Content-Type: 为 application/x-www-form-urlencoded编码的内容,提交方式GET、POST;

C) 该注解有两个属性: value、required; value用来指定要传入值的id名称,required用来指示参数是否必须绑定;

示例代码:

 

[java]  view plain copy
  1. @Controller  
  2. @RequestMapping("/pets" 
  3. @SessionAttributes("pet" 
  4. public class EditPetForm  
  5.   
  6.     // ...  
  7.   
  8.     @RequestMapping(method RequestMethod.GET)  
  9.     public String setupForm(@RequestParam("petId"int petId, ModelMap model)  
  10.         Pet pet this.clinic.loadPet(petId);  
  11.         model.addAttribute("pet"pet);  
  12.         return "petForm" 
  13.      
  14.   
  15.     // ...  


 

@RequestBody

该注解常用来处理Content-Type: 不是application/x-www-form-urlencoded编码的内容,例如application/json, application/xml等;

它是通过使用HandlerAdapter 配置的HttpMessageConverters来解析post data body,然后绑定到相应的bean上的。

因为配置有FormHttpMessageConverter,所以也可以用来处理 application/x-www-form-urlencoded的内容,处理完的结果放在一个MultiValueMap里,这种情况在某些特殊需求下使用,详情查看FormHttpMessageConverter api;

示例代码:

 

[java]  view plain copy
  1. @RequestMapping(value "/something"method RequestMethod.PUT)  
  2. public void handle(@RequestBody String body, Writer writer) throws IOException  
  3.   writer.write(body);  
  4.  

 

4、@SessionAttributes, @ModelAttribute

@SessionAttributes:

该注解用来绑定HttpSession中的attribute对象的值,便于在方法中的参数里使用。

该注解有value、types两个属性,可以通过名字和类型指定要使用的attribute 对象;

示例代码:

 

[java]  view plain copy
  1. @Controller  
  2. @RequestMapping("/editPet.do" 
  3. @SessionAttributes("pet" 
  4. public class EditPetForm  
  5.     // ...  
  6.  


 

@ModelAttribute

该注解有两个用法,一个是用于方法上,一个是用于参数上;

用于方法上时:  通常用来在处理@RequestMapping之前,为请求绑定需要从后台查询的model;

用于参数上时: 用来通过名称对应,把相应名称的值绑定到注解的参数bean上;要绑定的值来源于:

A) @SessionAttributes 启用的attribute 对象上;

B) @ModelAttribute 用于方法上时指定的model对象;

C) 上述两种情况都没有时,new一个需要绑定的bean对象,然后把request中按名称对应的方式把值绑定到bean中。


用到方法上@ModelAttribute的示例代码:

 

[java]  view plain copy
  1. // Add one attribute  
  2. // The return value of the method is added to the model under the name "account"  
  3. // You can customize the name via @ModelAttribute("myAccount")  
  4.   
  5. @ModelAttribute  
  6. public Account addAccount(@RequestParam String number)  
  7.     return accountManager.findAccount(number);  
  8.  

这种方式实际的效果就是在调用@RequestMapping的方法之前,为request对象的model里put(“account”, Account);

 


用在参数上的@ModelAttribute示例代码:

 

[java]  view plain copy
  1. @RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit"method RequestMethod.POST)  
  2. public String processSubmit(@ModelAttribute Pet pet)  
  3.      
  4.  
首先查询 @SessionAttributes有无绑定的Pet对象,若没有则查询@ModelAttribute方法层面上是否绑定了Pet对象,若没有则将URI template中的值按对应的名称绑定到Pet对象的各属性上。

 

补充讲解:

问题: 在不给定注解的情况下,参数是怎样绑定的?

通过分析AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter的源代码发现,方法的参数在不给定参数的情况下:

若要绑定的对象时简单类型:  调用@RequestParam来处理的。  

若要绑定的对象时复杂类型:  调用@ModelAttribute来处理的。

这里的简单类型指java的原始类型(boolean, int 等)、原始类型对象(Boolean, Int等)、String、Date等ConversionService里可以直接String转换成目标对象的类型;


下面贴出AnnotationMethodHandlerAdapter中绑定参数的部分源代码:

 

[java]  view plain copy
  1. private Object[] resolveHandlerArguments(Method handlerMethod, Object handler,  
  2.             NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception  
  3.   
  4.         Class[] paramTypes handlerMethod.getParameterTypes();  
  5.         Object[] args new Object[paramTypes.length];  
  6.   
  7.         for (int 0args.length; i++)  
  8.             MethodParameter methodParam new MethodParameter(handlerMethod, i);  
  9.             methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);  
  10.             GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());  
  11.             String paramName null 
  12.             String headerName null 
  13.             boolean requestBodyFound false 
  14.             String cookieName null 
  15.             String pathVarName null 
  16.             String attrName null 
  17.             boolean required false 
  18.             String defaultValue null 
  19.             boolean validate false 
  20.             Object[] validationHints null 
  21.             int annotationsFound 0 
  22.             Annotation[] paramAnns methodParam.getParameterAnnotations();  
  23.   
  24.             for (Annotation paramAnn paramAnns)  
  25.                 if (RequestParam.class.isInstance(paramAnn))  
  26.                     RequestParam requestParam (RequestParam) paramAnn;  
  27.                     paramName requestParam.value();  
  28.                     required requestParam.required();  
  29.                     defaultValue parseDefaultValueAttribute(requestParam.defaultValue());  
  30.                     annotationsFound++;  
  31.                  
  32.                 else if (RequestHeader.class.isInstance(paramAnn))  
  33.                     RequestHeader requestHeader (RequestHeader) paramAnn;  
  34.                     headerName requestHeader.value();  
  35.                     required requestHeader.required();  
  36.                     defaultValue parseDefaultValueAttribute(requestHeader.defaultValue());  
  37.                     annotationsFound++;  
  38.                  
  39.                 else if (RequestBody.class.isInstance(paramAnn))  
  40.                     requestBodyFound true 
  41.                     annotationsFound++;  
  42.                  
  43.                 else if (CookieValue.class.isInstance(paramAnn))  
  44.                     CookieValue cookieValue (CookieValue) paramAnn;  
  45.                     cookieName cookieValue.value();  
  46.                     required cookieValue.required();  
  47.                     defaultValue parseDefaultValueAttribute(cookieValue.defaultValue());  
  48.                     annotationsFound++;  
  49.                  
  50.                 else if (PathVariable.class.isInstance(paramAnn))  
  51.                     PathVariable pathVar (PathVariable) paramAnn;  
  52.                     pathVarName pathVar.value();  
  53.                     annotationsFound++;  
  54.                  
  55.                 else if (ModelAttribute.class.isInstance(paramAnn))  
  56.                     ModelAttribute attr (ModelAttribute) paramAnn;  
  57.                     attrName attr.value();  
  58.                     annotationsFound++;  
  59.                  
  60.                 else if (Value.class.isInstance(paramAnn))  
  61.                     defaultValue ((Value) paramAnn).value();  
  62.                  
  63.                 else if (paramAnn.annotationType().getSimpleName().startsWith("Valid"))  
  64.                     validate true 
  65.                     Object value AnnotationUtils.getValue(paramAnn);  
  66.                     validationHints (value instanceof Object[] (Object[]) value new Object[] {value});  
  67.                  
  68.              
  69.   
  70.             if (annotationsFound 1 
  71.                 throw new IllegalStateException("Handler parameter annotations are exclusive choices "  
  72.                         "do not specify more than one such annotation on the same parameter: " handlerMethod);  
  73.              
  74.   
  75.             if (annotationsFound == 0{// 若没有发现注解  
  76.                 Object argValue resolveCommonArgument(methodParam, webRequest);    //判断WebRquest是否可赋值给参数  
  77.                 if (argValue != WebArgumentResolver.UNRESOLVED)  
  78.                     args[i] argValue;  
  79.                  
  80.                 else if (defaultValue != null 
  81.                     args[i] resolveDefaultValue(defaultValue);  
  82.                  
  83.                 else  
  84.                     Class





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值