Handler + Context 架构，是Solon Web 的基础。在 Context （org.noear.solon.core.handle.Context）里可以获取：

* 请求相关的对象与接口
* 会话状态相关的对象与接口
* 响应相关的对象与接口

或者理解所有请求与响应相关的，都在它身上。关于架构方面，可以再看看[《想法与架构笔记》](/article/idea)

### 1、几种获取（或控制） Context 的方式

a) 通过 Controller 获取

```java
@Controller
public class HelloController{
    @Mapping("/hello")
    public String hello(Context ctx){
        //可以注入 ctx:Context
        return "Hello " + ctx.paramOrDefault("name", "world");
    }
}
```


b) 通过 Handler 或 Filter 或 RouterInterceptor 接口方式获取

```java
Solon.start(DemoApp.class, args, app->{
   app.router().get("/hello", ctx-> ctx.output("Hello " + ctx.paramOrDefault("name", "world"))); 
});

//或者，用以组件方式编写
@Mapping("/hello")
@Component
public class HelloHandler implements Handler{
    public void handle(Context ctx) throws Throwable{
        ctx.output("Hello " + ctx.paramOrDefault("name", "world"));
    }
}
```

c) 直接获取当前上下文（基于 ThreadLocal 实现）

```java
Context ctx = Context.current();
```

d) 获取当前线程的上下文，进行跨线程传递（一般在异步场景才用到）

```java
public class Demo {
    public void asyncDemo() {
        //获取当前线程的请求上下文
        Context ctx = Context.current();

        //跨线程传递当前请求上下文
        RunUtil.async(()->{
            ContextHolder.currentWith(ctx, ()->{
                //... Context.current() 有值...
            });
        });
    }
}
```

e) 设置空的当前上下文，并模拟数据（一般用在非 web 请求环境）


```java
Context ctx = ContextEmpty.create();

//模拟请求的数据（如果有需要）
ctx.headerMap().put("Header1", "1");
ctx.paramMap().put("Param1", "1");
ctx.pathNew("/path1");

ContextHolder.currentWith(ctx, ()->{
    //... Context.current() 有值...
});
```


### 2、几个特殊属性



| 属性 | 说明 | 备注 |
| -------- | -------- | -------- |
| ctx.mainHandler() | 获取主请求处理 | 路由器没有登记时，则为 null（基本属于 404 了） |
|      |      |      |
| ctx.controller()     | 获取当前控制器实例     | 如果不是 MVC 处理，则为 null     |
| ctx.action()     | 获取当前动作对象     | 如果不是 MVC 处理，则为 null     |
| ctx.action().method()     | 获取当前动作的执行函数包装     |      |
|      |      |      |
| ctx.errors     | 获取MVC处理异常     | action 执行完成后才会有值     |
| ctx.result     | 获取MVC处理结果     | action 执行完成后才会有值     |
|      |      |      |
| ctx.attr("output")   |  获取 string 输出内容    | 只要通过 ctx.output(str) 输出的都会有这个记录    |


提示：控制器里的 `@Mapping` 函数，即为 `Action`。


### 3、请求相关的接口


| 请求相关接口 | 说明 | 
| -------- | -------- | 
| -request()->Object     | 原始请求对象     | 
| -remoteIp() | 获取远程ip（也可能是代理的ip） |
| -remotePort() | 获取远程端口（也可能是代理的port） |
| -localPort()    | 获取本地端口（本地启动的port） |
| -realIp()->String     | 获取客户端真实IP     | 
| -isMultipart()-bool     | 是否为分段内容     | 
| -isMultipartFormData()->bool     | 是否为分段表单数据     | 
| -method()->String     | 获取请求方式     | 
| -protocol()->String     | 获取请求协议     | 
| -protocolAsUpper()->String     | 获取请求协议并大写     | 
| -url()->String     | 获取请求的URL字符串     | 
| -uri()->URI     | 获取请求的URI     | 
| -path()->String     | 获取请求的URI路径     | 
| -pathNew(String)     | 设置新路径     | 
| -pathNew()->String     | 获取新路径，不存在则返回原路径     | 
| -pathMap(String)->`Map<String,String>`     | 获取请求的URI路径变量,根据路径表达式     | 
| -pathAsUpper()->String     | 获取请求的URI路径并大写     | 
| -pathAsLower()->String     | 获取请求的URI路径并小写     | 
| -userAgent()>String     | 获取请求的UA     | 
| -contentLength()->long     | 获取内容长度     | 
| -contentType()->String     | 获取内容类型     | 
| -queryString()->String     | 获取查询字符串     | 
| -accept()->String     | 获取 Accept 头信息     | 
| -body()->String     | 获取body内容     | 
| -body(String)->String     | 获取body内容，并按指定字符串解码     | 
| -bodyNew()->String     | 获取新的body     | 
| -bodyNew(String)     | 设置新的body     | 
| -bodyAsBytes()->byte[]     | 获取body内容为byte[]     | 
| -bodyAsStream()->InputStream     | 获取body内容为Stream     | 
| -paramValues(String)->`String[]`     | 获取参数数组     | 
| -param(String)->String     | 获取参数     | 
| -paramOrDefault(String, String)->String     | 获取参数，并给定默认值     | 
| -paramAsInt(String)->int     | 获取参数并转为int     | 
| -paramAsInt(String, int)->int     | 获取参数并转为int, 并给定默认值     | 
| -paramAsLong(String)->long     | 获取参数并转为long     | 
| -paramAsLong(String, long)->long     | 获取参数并转为long,并给定默认值     | 
| -paramAsDouble(String)->double     | 获取参数并转为double     | 
| -paramAsDouble(String, double)->double     | 获取参数并转为double,并给定默认值     | 
| -paramAsDecimal(String)->BigDecimal     | 获取参数并转为BigDecimal     | 
| -paramAsDecimal(String, BigDecimal)->BigDecimal     | 获取参数并转为BigDecimal,并给定默认值     | 
| -paramAsBean(`Class<T>`)->T     | 获取参数并转为Bean     | 
| -paramMap()->`MultiMap<String>`     | 获取所有参数集合     | 
| -paramNames()->`Set<String>`      | 获取所有参数名字集合     | 
| -file(String)->UploadedFile     | 获取上传文件，第一个     | 
| -fileMap()->`MultiMap<UploadedFile>`     | 获取所有上传文件集合     | 
| -fileValues(String)->`UploadedFile[]`     | 获取上传文件，可能有多个     | 
| -fileNames()->`Set<String>`      | 获取所有上传文件名字集合     | 
| -filesDelete()     | 删除所有上传的临时缓冲文件（如果有）     | 
| -cookie(String)->String     | 获取 cookie     | 
| -cookieOrDefaul(String, String)->String     | 获取 cookie, 并给定默认值     | 
| -cookieValues(String)->`String[]`     | 获取 cookie 数组  | 
| -cookieMap()->`MultiMap<String>`     | 获取所有 cookie 集合     | 
| -cookieNames()->`Set<String>`      | 获取所有 cookie 名字集合     | 
| -header(String)->String     | 获取 header     | 
| -headerOrDefault(String, String)->String     | 获取 header,并给定默认值     | 
| -headerValues(String)->`String[]`     | 获取 header 数组  | 
| -headerMap()->`MultiMap<String>`      | 获取所有 header 集合     | 
| -headerNames()->`Set<String>`      | 获取所有 header 名字集合     | 


关于 MultiMap 接口的使用示例：


```java
//for（遍历）
for(KeyValues<String> kv: ctx.paramMap()){ //headerMap(), cookieMap()
    String       val  = kv.getValue();  //单值
    List<String> val2 = ky.getValues(); //多值
}

//add（添加）
ctx.paramMap().add("list", "a1")
ctx.paramMap().add("list", "a2")

ctx.paramValues("list") -> String[] //获取多值

//put（替换）
ctx.paramMap().put("item", "a1")

ctx.param("item") -> String //获取单值

```

### 4、响应相关的接口



| 响应相关接口 | 说明 | 
| -------- | -------- | 
| -response()->Object     | 原始响应对象     | 
| -charset(String)     | 设置字符集     | 
| -contentType(String)     | 设置内容类型     | 
| -contentTypeNew()     | 获取设置的新内容类型     | 
| -contentLength(long) | 设置内容长度 |
| -render(Object)     | 渲染数据（比如将对象渲染为 Json 并输出）     | 
| -render(String, Map)     | 渲染视图     | 
| -renderAndReturn(Object)->String     | 渲染数据并返回     | 
| -output(byte[])     | 输出 字节数组     | 
| -output(InputStream)     | 输出 流对象     | 
| -output(String)     |  输出 字符串     | 
| -output(Throwable)     | 输出 异常对象     | 
| -outputAsJson(String)     | 输出为json文本     | 
| -outputAsHtml(String)     | 输出为html文本     | 
| -outputAsFile(DownloadedFile)     | 输出为文件     | 
| -outputAsFile(File)     | 输出为文件     | 
| -outputStream()->OutputStream     | 获取输出流     | 
| -outputStreamAsGzip()->GZIPOutputStream     | 获取压缩输出流     | 
| -flush() | 冲刷 |
| -headerSet(String, String)     | 设置响应 header     | 
| -headerAdd(String, String)     | 添加响应 header     | 
| -headerOfResponse(String) | 获取响应 header |
| -headerValuesOfResponse(String) | 获取响应 header 数组 |
| -headerNamesOfResponse() | 获取响应 header 所有名字 |
| -cookieSet(String, String)     | 设置响应 cookie     | 
| -cookieSet(String, String, int)     | 设置响应 cookie     | 
| -cookieSet(String, String, String, int)     | 设置响应 cookie     | 
| -cookieSet(String, String, String, String, int)     | 设置响应 cookie     | 
| -cookieRemove(String)     | 移徐响应 cookie     | 
| -redirect(String)     | 302跳转地址     | 
| -redirect(String, int)     | 跳转地址     | 
| -forward(String)     | 服务端转换地址     | 
| -status()     | 获取输出状态     | 
| -status(int)     | 设置输出状态     | 

### 5、会话相关的接口



| 会话相关接口 | 说明 | 
| -------- | -------- | 
| -sessionState()->SessionState     | 获取 sessionState     | 
| -sessionId()->String     | 获取 sessionId     | 
| -session(String)->Object     | 获取 session 状态     | 
| -sessionOrDefault(String, T)->T     | 获取 session 状态（类型转换，存在风险）     | 
| -sessionAsInt(String)->int     | 获取 session 状态以 int 型输出     | 
| -sessionAsInt(String, int)->int     | 获取 session 状态以 int 型输出, 并给定默认值     | 
| -sessionAsLong(String)->long     | 获取 session 状态以 long 型输出     | 
| -sessionAsLong(String, long)->long     | 获取 session 状态以 long 型输出, 并给定默认值     | 
| -sessionAsDouble(String)->double     | 获取 session 状态以 double 型输出     | 
| -sessionAsDouble(String, double)->double     | 获取 session 状态以 double 型输出, 并给定默认值     | 
| -sessionSet(String, Object)     | 设置 session 状态     | 
| -sessionRemove(String)     | 移除 session 状态     | 
| -sessionClear()     | 清空 session 状态     | 

### 6、其它查询


| 其它相关接口 | 说明 | 
| -------- | -------- | 
| +current()->Context     | 获取当前线程的上下文     | 
| -getLocale()->Locale     | 获取地区     | 
| -setLocale(Locale)     | 设置地区     | 
| -setHandled(bool)     | 设置处理状态     | 
| -getHandled()     | 获取处理状态     | 
| -setRendered(bool)     | 设置渲染状态     | 
| -getRendered()     | 获取渲染状态     | 
| -attrMap()->Map     | 获取自定义特性并转为Map     | 
| -attr(String)->Object     | 获取上下文特性     | 
| -attrOrDefault(String, T)->T     | 获取上下文特性，并设定默认值     | 
| -attrSet(String, Object)     | 设置上下文特性     | 
| -attrSet(Map)     | 设置上下文特性     | 
| -attrClear()     | 清除上下文特性     | 
| -remoting()->bool     | 是否为远程调用     | 
| -remotingSet(bool)     | 设置是否为远程调用     | 
| -result:Object     | 用于在处理链中透传处理结果     | 
| -errors:Throwable     | 用于在处理链中透传处理错误     | 
| -controller()->Object     | 获取当前控制器     | 
| -action()->Action     | 获取当前动作     | 

