@TOC
MIME与Content-Type
MIME类型说明:
- 语法结构
Content-Type: [type]/[subtype];parameter
- type 有下面的形式:
Text:用于标准化地表示的文本信息,文本消息可以是多种字符集和或者多种格式的;
Multipart:用于连接消息体的多个部分构成一个消息,这些部分可以是不同类型的数据;
Application:用于传输应用程序数据或者二进制数据;
Message:用于包装一个E-mail消息;
Image:用于传输静态图片数据;
Audio:用于传输音频或者音声数据;
Video:用于传输动态影像数据,可以是与音频编辑在一起的视频数据格式。
MIME:
- MIME(Multipurpose Internet Mail Extensions) 多功能网际邮件扩充服务。
在把输出结果响应到浏览器上的时候或发送请求时表示发送的数据类型,浏览器必须启动适当的应用程序来处理这个输出文档。这可以通过MIME来完成。在HTTP中,MIME类型被定义在Content-Type header中。①告诉客户端实际返回的内容的内容类型 ②客户端告诉服务器实际发送的数据类型- 假设你要传送一个Microsoft Excel文件到客户端。那么这时的MIME类型是“application/vnd.mx-excel”。
- 在大多数情况下,这个文件将传送给Excel来处理(假设我们设定Excel为处理特殊MIME类型的应用程序)。
- 每个MIME类型由两部分组成,前面是数据的大类别,例如音频audio、图像image等,后面定义具体的种类。
- 常见的MIME类型:
①超文本标记语言文本.html text/html
②
普通文本.txt text/plain:text/plain的意思是将文件设置为纯文本的形式,浏览器在获取到这种文件时并不会对其进行处理。③RTF文本.rtf application/rtf ④GIF图形.gif image/gif ⑤JPEG图形.ipeg,.jpg image/jpeg ⑥au声音文件.au audio/basic ⑦MIDI音乐文件mid,.midi audio/midi,audio/x-midi ⑧RealAudio音乐文件.ra,.ram audio/x-pn-realaudio ⑨MPEG文件.mpg,.mpeg video/mpeg ⑩AVI文件.avi video/x-msvideo ⑪GZIP文件.gz application/x-gzip ⑫TAR文件.tar application/x-tar
HTTP请求中几种常见的Content-Type类型:
- POST请求的消息主体放在entitybody中,服务端根据请求头中的Content-Type字段来获取消息主体的编码方式,进而进行解析数据。
- application/x-www-form-urlencoded:
①最常见的 POST 提交数据的方式,原生Form表单,
如果不设置 enctype属性,默认为application/x-www-form-urlencoded 方式提交数据。②首先Content-Type被指定为application/x-www-form-urlencoded;其次,提交的表单数据会转换为键值对并按照key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL转码。大部分服务端语言都对这种方式有很好的支持。③另外如利用AJAX 提交数据时,也可使用这种方式。例如 jQuery,Content-Type默认值都是”application/x-www-form-urlencoded;charset=utf-8”。④当action为get时候,浏览器用x-www-form-urlencoded的编码方式把form数据转换成一个字串(name1=value1&name2=value2…),然后把这个字串append到url后面,用?分割,加载这个新的url。 - multipart/form-data:
①另一个常见的 POST 数据提交的方式,
Form 表单的 enctype设置为multipart/form-data,它会将表单的数据处理为一条消息,以标签为单元,用分隔符(这就是boundary的作用)分开,类似我们上面Content-Type中的例子。②由于这种方式将数据有很多部分,它既可以上传键值对,也可以上传文件,甚至多个文件。当上传的字段是文件时,会有Content-Type来说明文件类型;Content-disposition,用来说明字段的一些信息。每部分都是以–boundary开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(字段、文本或二进制等)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以–boundary– 标示结束。 - application/json:
①
Content-Type: application/json作为响应头比较常见。实际上,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串,其中一个好处就是JSON格式支持比键值对复杂得多的结构化数据。由于 JSON 规范的流行,除了低版本 IE之外的各大浏览器都原生支持JSON.stringify,服务端语言也都有处理 JSON 的函数,使用起来没有困难。 Google 的AngularJS 中的 Ajax 功能,默认就是提交 JSON 字符串。 ②在PostMan需要点击raw(未加工)再选择JSON。 - text/xml:
①
XML的作用不言而喻,用于传输和存储数据,它非常适合万维网传输,提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据,在JSON出现之前是业界一大标准(当然现在也是),相比JSON的优缺点大家有兴趣可以上网search。因此,在POST提交数据时,xml类型也是不可缺少的一种,虽然一般场景上使用JSON可能更轻巧、灵活。 ②在PostMan需要点击raw(未加工)再选择XML。 - binary (application/octet-stream): ①在Chrome浏览器的Postman工具中,还可以看到”binary“这一类型,指的就是一些二进制文件类型。如application/pdf,指定了特定二进制文件的MIME类型。就像对于text文件类型若没有特定的子类型(subtype),就使用text/plain。类似的,二进制文件没有特定或已知的 subtype,即使用application/octet-stream,这是应用程序文件的默认值,一般很少直接使用 。 ②对于application/octet-stream,只能提交二进制,而且只能提交一个二进制,如果提交文件的话,只能提交一个文件,后台接收参数只能有一个,而且只能是流(或者字节数组)。 ③很多web服务器使用默认的 application/octet-stream来发送未知类型。出于一些安全原因,对于这些资源浏览器不允许设置一些自定义默认操作,导致用户必须存储到本地以使用。一般来说,设置正确的MIME类型很重要。
- 总结:
①form-data既可以上传键值对,也可以上传文件。当上传的字段是文件时。
②Binary每次只能传一个附件,而Form-data可以传多个。
③
由于有boundary隔离,所以multipart/form-data既可以上传文件,也可以上传键值对,它采用了键值对的方式,所以可以上传多个文件。④PostMan中的raw可以上传任意格式的文本,可以上传text、json、xml、html等。
Content-Type和MIME的关系:
content_type是mimetype的别名。从历史上讲,此参数仅称为mimetype,但是由于它实际上是HTTP Content-Type标头中包含的值,因此它还可以包含字符集编码,这使其不仅限于MIME类型规范。如果指定了mimetype(不是None),则使用该值。否则,将使用content_type。- 如果两者均未给出,则使用DEFAULT_CONTENT_TYPE设置。
- 具体来说,Content-Type:是几个MIME头中的一种。“ Mimetype”确实听起来已经过时了,但是对MIME本身的引用却不是。如果可以的话,将该部分称为后向兼容性。
这纯粹是一个术语问题,与语法无关。
Ajax请求($.ajax()为例)中data属性传参数的形式
实现Ajax提交数据进行请求,其中data属性设置传参的方法有好几种形式,如下:
//第一种写法(把参数拼接在URL中,data属性设为空{ })
function getFormInfo(){
var name='wen';
var user='chen';
$.ajax({
url: "/login/authenticate?name="+name+"&user="+user,
type: "POST",
data:{},
dataType: "json",
success: function(data){
},
error:function(err){
console.log(err.statusText);
console.log('异常');
}
});
}
//第二种写法(参数写成json数据形式)
function getFormInfo(){
$.ajax({
url: "http://192.168.10.32:6833/login/authenticate",
type: "POST",
data:{
name:'chem',
user:'wen'
},
cache:false,
dataType: "json",
success: function(data){
},
error:function(err){
}
});
}
第三种写法(根据表单id属性,把表单封装数据,调用JQuery的serialize()方法序列化为字符串) 前提是:发送请求的必须是一个form表单,而且表单内要做参数的标签必须具有name属性,因为name属性会被认为请求参数名
//代码如下
function getFormInfo(){
var params=$('#login').serialize(); //把id为login的form表单里的参数自动封装为参数传递
console.log(params);
$.ajax({
url: "http://192.168.10.32:6833/login/authenticate",
type: "POST",
data:params,
cache:false,
dataType: "json",
success: function(data){
},
error:function(err){
}
});
}
//第四种写法(拼接data)
function getFormInfo(){
var name='chen';
var user='wen';
$.ajax({
url: "http://192.168.10.32:6833/login/authenticate",
type: "POST",
data:'name='+name+'&user='+user,
cache:false,
dataType: "json",
success: function(data){
},
error:function(err){
}
});
}
- 需要引入:<script type="text/javascript" src="serializeJSON.js"></script>
//第五种写法(表单序列化为json数据)
function getFormInfo(){
var params=$('#login').serializeJSON();
console.log(params);
$.ajax({
url: "http://192.168.10.32:6833/login/authenticate",
type: "POST",
data:params,
cache:false,
dataType: "json",
success: function(data){
},
error:function(err){
}
});
}
//第六种写法(既有全部直接获取表单中的数据又有单独出来的数据)
function getFormInfo(){
var params=$('#login').serializeJSON();
console.log(params);
params.height='20';
$.ajax({
url: "http://192.168.10.32:6833/login/authenticate",
type: "POST",
data:params,
cache:false,
dataType: "json",
success: function(data){
},
error:function(err){
}
});
}
form表单提交时怎么实现URL拼接的?
简介:
- form 默认 get, 表单提交的时候会把所有有 name 属性的表单控件的值提取为 name=value 拼接到 action属性作为查询参数。
header中Content-Disposition的作用与使用方法
简介:
- Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。
- Content-disposition其实可以控制用户请求所得的内容存为一个文件的时候提供一个默认的文件名,文件直接在浏览器上显示或者在访问时弹出文件下载对话框。
- 格式说明: content-disposition = "Content-Disposition" ":"disposition-type *( ";" disposition-parm )
- 字段说明:Content-Disposition为属性名disposition-type是以什么方式下载,如attachment为以附件方式
- 下载disposition-parm为默认保存时的文件名服务端向客户端游览器发送文件时,如果是浏览器支持的文件类型,一般会默认使用浏览器打开,比如txt、jpg等,会直接在浏览器中显示,如果需要提示用户保存,就要利用Content-Disposition进行一下处理,关键在于一定要加上attachment:
Response.AppendHeader("Content-Disposition","attachment;filename=FileName.txt");
- 备注:这样浏览器会提示保存还是打开,即使选择打开,也会使用相关联的程序比如记事本打开,而不是IE直接打开了。Content-Disposition就是当用户想把请求所得的内容存为一个文件的时候提供一个默认的文件名。
- 那么由上可知具体的例子: Content-Disposition: attachment; filename="filename.xls"当然filename参数可以包含路径信息,但User-Agnet会忽略掉这些信息,只会把路径信息的最后一部分做为文件名。当你在响应类型为application/octet-stream情况下使用了这个头信息的话,那就意味着你不想直接显示内容,而是弹出一个"文件下载"的对话框,接下来就是由你来决定"打开"还是"保存"了。
注意事项:
- 当代码里面使用Content-Disposition来确保浏览器弹出下载对话框的时候。
response.addHeader("Content-Disposition","attachment");一定要确保没有做过关于禁止浏览器缓存的操作。
- 代码如下:
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "No-cache");
response.setDateHeader("Expires", 0);
- 不然会发现下载功能在opera和firefox里面好好的没问题,在IE下面就是不行。
null和undefined与后台问题
简述:
- 关于返回 null 的问题,首先处理 js 中的 null 和 undefined
对于 resp={"code":0,"data":{"name":"张三"}}
resp.data.info 返回 undefined
对于 resp={"code":0,"data":{"name":"张三",info:null}}
resp.data.info 返回 null
但是不同点在于
前一种 js 认为 resp.data 上没有 info 这个属性
后一种 js 认为 resp.data 上有 info 这个属性
一般情况下不会有问题,但是遇到遍历等情况时会出现差异
for(i in resp.data){ console.log(i); }
更可贵的一点是:
对于 resp={"code":0,"data":{"name":"张三",info:undefined}}
resp.data.info 同样返回 undefined 但是 js 认为 resp.data 上有 info 这个属性
- 为了避免 null 值和 undefined 值的混乱造成的问题。
在拟定接口标准时将不传、undefined、null 认为表达一个概念 因为不传可以在一定范围内减少传输数据量。所以统一为不传。即统一使用 resp={"code":0,"data":{"name":"张三"}} 的形式表示无数据- 关于集合,
若集合中无元素则返回 [],不要试图使用留空表达特殊状态,特殊状态另起一个属性表达。 - 关于字符串,
使用空字符串表示没有该值或者初始值,不要试图使用留空表达特殊状态,特殊状态另起一个属性表达。 特殊情况单独约定
null和undefined出现的情况:
- undefined出现情况:
①
变量未赋值②数组未赋值③对象未赋值③函数获取不到值 - null出现情况:
①
作为对象原型链的终点②作为函数的参数,表示该函数的参数不是对象
后台问题:
- 对于集合和数组作为返回值,使用长度为零的数组或者集合,而不是null。
- 字符串作为返回值,使用空字符串来代替null。
- 任何在逻辑上表示查找(search或者get)的意思时,应该返回null。
- 当空对象与其他返回对象有一样的行为和意义时,使用空对象
- 链接:方法应该返回空对象还是null
Smart Admin
简介:
- SmartAdmin由河南·洛阳1024创新实验室团队研发的一套互联网企业级的通用型中后台解决方案!使用最前沿的前后台技术栈SpringBoot和Vue,前后端分离,我们开源一套漂亮的代码和一套整洁的代码规范,
- 让大家在这浮躁的代码世界里感受到一股把代码写好的清流!同时又让开发者节省大量的时间,减少加班,快乐工作,热爱生活。SmartAdmin让你从认识到忘不了,绝对是你最想要的!
- 帮助文档:关于SmartAdmin
- 技术体系: ①前端:Vue + Vue-Router + Vuex + ViewUI + vue-enum ②后端:SpringBoot2 + Mybatis-plus + jwt + druid + mysql ③前端代码规范smart-front-standard -guide(大力推荐) ④基于阿里规范之上的后端规范smart-backend-standard-guide(大力推荐)
前端特点:
- 高质量的代码、代码结构、和代码注释
- 漂亮的UI,菜单栏、标签页,体验、交互更好用的员工、部门、角色、菜单管理等等
- 优化基于Keepalive的标签页,做到标签页该缓存的时候缓存,比如左右切换等,不该缓存的时候不缓存,比如新建,表单提交结束等
- 前端常量维护: vue-enum,拒绝出现魔法数字,代码不可维护的现象
- 全新的基于前端的权限设计(忘掉传统的权限设计吧,已经不适合这个前端时代)
- 基于websocket的在线人数
- 支持一级、二级、三级菜单,四级菜单以及搜索功能
- 其他功能:邮件、富文本、消息、系统配置等等
后端特点:
- 高质量的Java代码、分包结构、和代码注释
- 业内独创的请求返回码维护,非常值得一看
- 基于一个注解和controller的权限设计放弃更复杂的shiro,以及一套数据权限支持
- 四层架构(controller, service, manager, dao)
- 代码阅读性强、扩展性极高的员工、部门、角色、菜单管理
- 基于LRU策略的内存级权限缓存
- 配合前端vue-enum的swagger文档注解
- 心跳服务,让你发现有哪些机器再跑,哪些人在偷偷的跑你的Job
- 自定义的quartz job添加和修改,方便测试人员测试 -smart-reload,为系统预留钩子,动态加载,在不重启程序前提下执行一些代码,你懂的
前端代码规范:
- 文件、文件夹、目录结构、组建、变量等等怎么命名
- html、css、less等如何规范
- vue项目目录结构如何划分
- router和store该怎么划分扩展性更好
- vue组件规范该选择哪些
后端代码规范:
- 四层架构(controller, service, manager, dao) 是什么,为什么要有四层
- 各个层的代码该怎么写才能让团队配合默契,高度一致
- vo, bo, dto, entity ,各种javabean 怎么区分和使用
- spring的 @Transactional 你用对了吗
- 方法参数个数、注释、todo这些也要有规范,你遵守过吗
Springboot与Vue整合
Vue打包部署:
- 安装node(最新版)
- 测试:输入node -v npm -v
- 进入vue项目根路径下
- 下载必须的依赖:npm install
- 这时分为开发环境或生产环境:
①
开发环境(Node启动http服务器):<1>直接运行:npm run dev <2>node会运行一个http服务器②
生产环境(nginx):<1>打包:npm run build
<2>成功后vue项目下会出现一个dist的文件夹 <3>下载nginx服务器,并放上去 ③生产环境(tomcat非嵌入式):<1>打包:npm run build
<2>成功后vue项目下会出现一个dist的文件夹 <3>下载tomcat服务器,并放上去 ④生产环境(tomcat嵌入式):<1>打包:npm run build
<2>成功后vue项目下会出现一个dist的文件夹 <3>放到springboot(嵌入式tomcat)的静态资源文件夹中 - 关于VUE框架的baseUrl与proxy:
①baseUrl是在你发出请求的时候添加在请求的前面的字段,他指向你要请求的地址。
<1>它属于构建(build)里的属性,针对打包后使用的。②proxy里面配置本地开发要监听的地址与端口,监听到了就拦截,并且还可以配置具体拦截哪些路径的正则,拦截下来后用target的字段进行转发,去请求服务端返回数据的地址,可以配置是否允许跨域与时候使用websock,并且可以对拦截到的端口后的路径进行rewrite。<1>它属于开发(dev)里的属性,针对开发环境使用的。<2>target属性设置访问后端的前缀url。
------------------------------------index.js------------------------------------
'use strict'
const path = require('path')
module.exports = {
build: {
env: require('./prod.env'),
index: path.resolve(__dirname, '../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: './',
productionSourceMap: true,
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
bundleAnalyzerReport: process.env.npm_config_report
},
dev: {
env: require('./dev.env'),
port: process.env.PORT || 8088,
autoOpenBrowser: true,
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/v1': {
target: 'https://nanerpc.rossopharm.com/v1',//接口域名-测试服
// target:'http://localhost:8089/v1',
changeOrigin: true,//是否允许跨越
pathRewrite: {
}
}
},
cssSourceMap: false
}
}
------------------------------------prod.env.js------------------------------------
'use strict'
module.exports = {
NODE_ENV: '"production"',
BASE_URL: '"http://localhost:8089/v1"' //本地测试环境
}
Springboot与vue整合注意细节:
- 链接: ①Springboot和vue整合 ②Spring Boot整合Vue,解决静态资源映射,页面刷新失效,路径配置等问
- 若是放到不同服务器的话则需要处理跨域问题。
- 此外当没有使用额外http服务器时,使用springoot映射时只能通过springboot对于静态资源和模板的映射规则进行映射。因此此时可以在springboot中进行额外的配置。
- 设置首页
(不设置也行,不过需要每次手敲进入静态资源的目录访问index.html):
---------------新增IndexController设置首页---------------
@Controller
@RequestMapping("")
public class IndexController {
@RequestMapping("/")
public String index() {
return "index";
}
}
- 静态资源访问:
---------------新增静态资源映射---------------
@Configuration
public class MvcConfig extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
}
- springboot+vue生产环境不能刷新页面问题:
①刷新页面会报
There was an unexpected error (type=Not Found, status=404)②但是开发环境是可以访问的,这是因为开发环境起了2个Server:Vue+SpringBoot。而生产环境就只有一个SpringBoot,于是刷新请求发送给SpringBoot,但是这些Route实际上是在Vue中定义的,自然就找不到了。 ③vue编译时其实把所有的Route打包在了index.html中,所以只要在SpringBoot中增加如下代码,这样当Route找不到的时候都会去index.html尝试,正确的url就都可以刷新了
@Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return (container -> {
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/index.html");
container.addErrorPages(error404Page);
});
}
vue项目结构:
HTTP传参
简介:
此处不考虑有文件的情况),不论Content-Type的类型,不论,get或post请求,只要参数不是字符串类型,浏览器都会对参数做field1=a&field2=b的处理,将参数转为此结构的字符串后传给服务端。- 但是如果参数本就是一串字符,浏览器便不会再对参数做处理,直接将原始字符串传到后台,get请求可以从QueryString中获取到原始字符串,post请求可以从请求体中取到原始字符串。
- Content-Type只是浏览器告诉后台如何去处理数据,并不会帮我们将请求参数转为Content-Type指定的类型,需要我们自己将Content-Type指定的类型与实际的参数形式对应起来。若你的Content-Type指定的是application/json,但是参数格式却不是json,浏览器不会帮你转为json格式,只是到了后台,发现Content-Type是application/json,便以json格式来解析数据,但实际数据格式并非json,所以后台报错。
- 链接:关于http请求传参的总结